Skip to content

PR: Implement Request Page Components (Card, Requests, Reject Modal)#232

Open
codewkaushik404 wants to merge 46 commits intoOpenLake:mainfrom
codewkaushik404:feature/certificate
Open

PR: Implement Request Page Components (Card, Requests, Reject Modal)#232
codewkaushik404 wants to merge 46 commits intoOpenLake:mainfrom
codewkaushik404:feature/certificate

Conversation

@codewkaushik404
Copy link

@codewkaushik404 codewkaushik404 commented Feb 20, 2026

Changes Introduced

  • Added:
    • Request page layout and structure.
    • Reusable Card component for displaying request details.
    • Requests component to manage and render multiple request items.
    • Modal component for rejecting requests with proper UI interaction.
  • Updated:
    • Integrated request components into the main workflow.
    • Connected modal state handling with request actions.

Why This Change?

  • Problem: The application lacked a structured interface for viewing and managing incoming requests.
  • Solution: Implemented a dedicated Requests page with modular components and a rejection modal to improve request handling.
  • Impact:
    • Improves admin workflow by providing a clean UI to review and manage requests.
    • Enhances user experience with structured request cards.
    • Adds rejection functionality through an interactive modal.

Screenshots / Video (if applicable)

Before After
N/A
image image |

Testing

  • [] Ran unit tests and all passed (npm test in the relevant directory).
  • Manually tested the following scenarios:
    • [x]Test Case 1:** Open Requests page → Requests render correctly.
    • [x]Test Case 2:** Click Reject → Modal opens successfully.
    • [x]Test Case 3:** Confirm rejection → Modal closes and state updates correctly.
  • Tested on different browsers (Chrome, Firefox) for UI changes.
  • Verified there are no new console warnings or errors.

Documentation Updates

  • Updated the README.md with new instructions.
  • Added clear code comments where logic is complex.
  • N/A

Checklist

  • I have created a new branch for this PR (git checkout -b feature/request-page-components).
  • I have starred the repository.
  • My code follows the project's coding style and conventions.
  • My commit messages are clear and follow the project's guidelines.
  • I have performed a self-review of my own code.
  • I have added tests that prove my fix is effective or that my feature works.
  • All new and existing tests passed locally with my changes.
  • This PR introduces no breaking changes (or they are clearly documented).

Deployment Notes

  • Requires a database migration/schema update.
  • Requires new environment variables to be set.
  • N/A

💬Additional Notes

  • Components are designed to be reusable for future request-related features.
  • UI structure allows easy integration of approval or additional action workflows in future updates.

Summary by CodeRabbit

  • New Features

    • Google + username/password authentication; token and session support.
    • Certificate workflow: create/manage batches, generate/view/download certificates, templates management.
    • Requests system: submit/review/approve/reject with dedicated UI and pages.
    • New dashboard sections: Certificates and Requests pages; batches management UI.
  • Improvements

    • Stronger onboarding and login validation; clearer auth responses and redirects.
    • Sidebar, navigation and layout refinements; improved profile photo flows.
  • Bug Fixes

    • Safer session/cookie handling and startup error reporting.

Switched to session-based authentication and configured persistent session storage with connect-mongo.
…ession-based authentication and configured persistent session storage with connect-mongo.
@vercel
Copy link

vercel bot commented Feb 20, 2026

@codewkaushik404 is attempting to deploy a commit to the openlake's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Split monolithic backend schema into multiple model files, added certificate batching (models, controllers, routes, validation, services), introduced Passport/local+Google and JWT cookie auth with session store, and added frontend pages/components for certificates, requests, templates and batching UI.

Changes

Cohort / File(s) Summary
Model decomposition & new models
backend/models/schema.js, backend/models/userSchema.js, backend/models/feedbackSchema.js, backend/models/achievementSchema.js, backend/models/eventSchema.js, backend/models/positionSchema.js, backend/models/positionHolderSchema.js, backend/models/organizationSchema.js, backend/models/templateSchema.js
Removed many exports from barrel schema.js and added dedicated model files (User, Feedback, Achievement, Event, Position, PositionHolder, OrganizationalUnit, Template) with schemas, indexes, timestamps, and password hashing hook.
Certificate system (backend)
backend/models/certificateSchema.js, backend/controllers/certificateBatchController.js, backend/controllers/certificateController.js, backend/routes/certificateRoutes.js, backend/utils/batchValidate.js, backend/services/*.js, backend/utils/httpError.js
New CertificateBatch & Certificate models, batch creation/retrieval controllers with Zod validation, approver/user resolution services, HttpError helper, and routes wired for batch endpoints.
Certificate system (frontend)
frontend/src/services/batch.js, frontend/src/Components/Batches/..., frontend/src/pages/batchesPage.jsx, frontend/src/Components/Certificates/..., frontend/src/pages/certificatesPage.jsx
Frontend services and many UI components/pages for batches and certificates (cards, modal, select, list, page) added and wired to new APIs.
Auth & session changes
backend/config/passportConfig.js, backend/models/passportConfig.js, backend/middlewares/isAuthenticated.js, backend/utils/authValidate.js, backend/index.js, backend/config/db.js, backend/routes/auth.js
Added Passport Google + Local flows, session serialization, jwtIsAuthenticated cookie middleware, express.json + cookie-parser + MongoStore session store, Zod validators, and reworked login/register/google flows.
Services & utilities
backend/services/event.service.js, backend/services/organization.service.js, backend/services/template.service.js, backend/services/user.service.js, backend/utils/httpError.js
New service layer functions to centralize event/template/organization/user lookups and error semantics used by batch flows.
Seed & DB tooling
backend/seed.js
Large seeding overhaul: updated imports to new models and added seeding for templates, certificate batches, and certificates plus revised user/position seeding logic.
Backend routing & imports refactor
backend/controllers/..., backend/routes/... (many files)
Updated many controllers/routes to import models from individual files instead of the barrel; switched middleware imports to named exports and added guards/404s in places.
Frontend pages, contexts & UI
frontend/src/pages/requestsPage.jsx, frontend/src/Components/Requests/..., frontend/src/Components/Templates/..., frontend/src/Components/Batches/..., frontend/src/context/RequestContext.js, frontend/src/config/dashboardComponents.js, frontend/src/config/navbarConfig.js
Added Requests & Templates UIs, RequestContext, batch/template components, updated dashboard mappings and navbar entries to include certificates/requests/templates.
Misc & tooling
.husky/pre-commit, backend/package.json, assorted small frontend/backend files
Simplified pre-commit script, dependency changes (added bcrypt, connect-mongo, cookie-parser, zod, uuid; removed body-parser, passport-local-mongoose), minor imports/formatting updates.

Sequence Diagram(s)

sequenceDiagram
  participant Client as rgba(60,120,216,0.5)
  participant Server as rgba(40,167,69,0.5)
  participant Auth as rgba(255,193,7,0.5)
  participant DB as rgba(220,53,69,0.5)

  Client->>Server: POST /api/batches/create-batch (batch payload, cookie)
  Server->>Auth: jwtIsAuthenticated -> verify cookie token
  Auth-->>Server: req.user
  Server->>DB: Event.findById(eventId), Template.findById(templateId)
  DB-->>Server: event, template
  Server->>Server: getUserPosition(req.user.id) -> resolve role/unit
  Server->>DB: User.find({_id: users[]}) (validate recipients)
  DB-->>Server: matched users
  Server->>Server: getApprovers(category) -> resolve gensec & president
  Server->>DB: CertificateBatch.create({...})
  DB-->>Server: created batch
  Server->>Client: 201 { message: "Batch created", batchId }
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • harshitap1305

Poem

🐇 I hopped through code and dug the lair,

Models split and routes laid bare.
Batches, certs, and passport keys,
Frontend dances, contexts breeze.
A carrot crunch for CI's care.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (15)
backend/config/db.js (1)

11-13: ⚠️ Potential issue | 🟠 Major

Server starts silently without a DB connection on failure.

The catch block only logs the error and returns normally. When connectDB is awaited in index.js at startup, a connection failure is silently absorbed and the HTTP server boots without a live database, causing all subsequent DB operations to fail at request time.

🛡️ Proposed fix
  } catch (error) {
    console.error("MongoDB Connection Error:", error);
+   process.exit(1);
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/config/db.js` around lines 11 - 13, The catch in connectDB currently
only logs the error which lets startup continue without a DB; update the
connectDB error handling (in backend/config/db.js, function connectDB) to fail
fast by either rethrowing the caught error or calling process.exit(1) after
logging so the awaited call in index.js rejects and the server does not start
without a DB; ensure you preserve the existing log (console.error) but then
propagate the failure (throw error or return Promise.reject(error)) so callers
see the failure.
backend/controllers/analyticsController.js (2)

379-383: ⚠️ Potential issue | 🟠 Major

Unsafe property access on populated fields will crash the endpoint for orphaned references.

If a referenced user_id or position_id document has been deleted, Mongoose .populate() silently sets those fields to null. Accessing .personal_info.name, .username, or .title on null throws a TypeError, taking down the entire getClubCoordinatorAnalytics response.

🛡️ Proposed fix
-currentPositionHolders: team.map(h => ({
-  name: h.user_id.personal_info.name,
-  username: h.user_id.username,
-  position: h.position_id.title
-})),
+currentPositionHolders: team
+  .filter(h => h.user_id && h.position_id)
+  .map(h => ({
+    name: h.user_id.personal_info?.name,
+    username: h.user_id.username,
+    position: h.position_id.title,
+  })),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/analyticsController.js` around lines 379 - 383, The
current mapping in getClubCoordinatorAnalytics that builds
currentPositionHolders from team.map(h => ({ name: h.user_id.personal_info.name,
username: h.user_id.username, position: h.position_id.title })) will throw when
populated user_id or position_id are null; update the mapping to defensively
handle orphaned references by checking for null before property access (e.g.,
use optional chaining or explicit null checks on h.user_id and h.position_id),
provide safe fallback values like 'Unknown' or skip entries, and keep the symbol
names team, currentPositionHolders, getClubCoordinatorAnalytics and the map
callback intact so the change is easy to locate.

233-233: ⚠️ Potential issue | 🟡 Minor

Typo: "Analyltics" → "Analytics" in the error log message.

✏️ Proposed fix
-console.error("President Dashboard Analyltics Error:", error);
+console.error("President Dashboard Analytics Error:", error);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/analyticsController.js` at line 233, Fix the typo in the
error log inside analyticsController.js: update the console.error call that
currently logs "President Dashboard Analyltics Error:" to correctly read
"President Dashboard Analytics Error:" so the message uses "Analytics" (locate
the console.error(...) in the President Dashboard error handling block).
backend/models/passportConfig.js (1)

55-66: ⚠️ Potential issue | 🟠 Major

serializeUser stores the full user object in the session — PII leak risk.

done(null, user) serializes the entire Mongoose document (email, name, profile picture, role, etc.) into the session store. Any session store breach (or accidental log) exposes full user PII. The standard and safe pattern is to serialize only the opaque ID; the deserializer already fetches the fresh record from the DB on every request so nothing is lost.

🔒 Proposed fix
 passport.serializeUser((user, done) => {
-  done(null, user);
+  done(null, user._id.toString());
 });

 passport.deserializeUser(async (userKey, done) => {
   try {
-    let user = await User.findById(userKey._id);
+    let user = await User.findById(userKey);
     done(null, user);
   } catch (err) {
     done(err);
   }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/passportConfig.js` around lines 55 - 66, serializeUser
currently stores the whole user object (done(null, user)) which leaks PII;
change passport.serializeUser to only serialize the user's ID (e.g., done(null,
user._id || user.id)) and update passport.deserializeUser to accept that id
(userId) and call User.findById(userId) (keeping the existing try/catch and
done(null, user) behavior); also add a defensive null/validation check in
deserializeUser to call done(null, false) or done(new Error(...)) if the id is
missing or no user is found.
backend/routes/orgUnit.js (1)

153-153: ⚠️ Potential issue | 🔴 Critical

Transaction atomicity broken: save(session) should be save({ session }).

Mongoose's Document.prototype.save() expects save([options]) where options.session is the ClientSession. Passing the ClientSession directly as the first argument means the session is not properly registered with the save operation, so newUnit is saved outside the transaction. If existingUser is found afterward and abortTransaction() is called, newUnit remains committed — leaving an orphaned org-unit.

Compare with the correct form on line 178: await newUser.save({ session });.

🐛 Proposed fix
-await newUnit.save(session);
+await newUnit.save({ session });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/orgUnit.js` at line 153, The newUnit save is passing the
ClientSession object directly (newUnit.save(session)), which causes the document
to be written outside the transaction; change the call to pass an options object
with the session (i.e., use newUnit.save({ session })) so the save participates
in the transaction; update the save invocation near the newUnit creation (same
area that checks existingUser and may call abortTransaction) to match how
newUser.save({ session }) is used.
backend/routes/events.js (3)

271-280: ⚠️ Potential issue | 🟠 Major

GET /:id silently returns null (HTTP 200) when the event is not found.

findById() returns null for a missing document; res.json(null) sends an HTTP 200 with a null body rather than a proper 404. Callers cannot distinguish "event exists but is empty" from "no such event".

🐛 Proposed fix
  const event = await Event.findById(req.params.id)
    .populate("organizing_unit_id", "name")
    .populate("organizers", "personal_info.name");
+ if (!event) return res.status(404).json({ message: "Event not found." });
  res.json(event);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 271 - 280, The GET handler for
router.get("/:id") uses Event.findById(...) and blindly returns res.json(event),
which sends HTTP 200 with a null body when not found; update the async route
handler to check the result of Event.findById (the variable event) and if it's
null respond with res.status(404).json({ message: "Event not found" })
(otherwise continue to return the event), keeping the existing catch block for
server errors and error logging intact; locate this logic in the
router.get("/:id", async (req, res) => { ... }) function and modify the
post-findById flow accordingly.

444-477: ⚠️ Potential issue | 🟡 Minor

Remove debug console.log dumps from the production PUT route.

Lines 444–467 log the full request body and event document (before and after update) on every call. This is a performance overhead, can expose sensitive event data in server logs, and should not be in a merge-ready branch.

🐛 Proposed fix
-   // 🔍 DEBUG LOGS - START
-   console.log("\n=== 📝 UPDATE EVENT DEBUG ===");
-   console.log("Event ID:", eventId);
-   console.log("Updates received (full body):", JSON.stringify(updates, null, 2));
-   console.log("Number of fields to update:", Object.keys(updates).length);
-   console.log("Fields being updated:", Object.keys(updates));
-   console.log("========================\n");
-   const eventBefore = await Event.findById(eventId);
-   console.log("Event BEFORE update:", JSON.stringify(eventBefore, null, 2));
    const event = await Event.findByIdAndUpdate(eventId, updates, {
      new: true,
      runValidators: true,
    });
-   console.log("\n=== ✅ UPDATE RESULT ===");
-   console.log("Event AFTER update:", JSON.stringify(event, null, 2));
-   console.log("Update successful:", !!event);
-   console.log("========================\n");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 444 - 477, Remove the development
debug console.log dumps from the update PUT handler so production logs don't
contain full request bodies or database documents: delete the console.log block
that prints "=== 📝 UPDATE EVENT DEBUG ===" and the subsequent logs that print
eventId, updates, Event.findById result (eventBefore), and the "=== ✅ UPDATE
RESULT ===" logs; keep the actual update logic using Event.findByIdAndUpdate and
the existing try/catch and error handling (the function surrounding
Event.findById, Event.findByIdAndUpdate, and the catch block should remain
unchanged), but replace verbose debug prints with concise logging if needed
(e.g., a single info or error log) or remove them entirely.

415-420: ⚠️ Potential issue | 🟡 Minor

Add reviewed_at field to track review timestamp separately from request submission time.

requested_at is intended to capture when the room request was originally submitted (defined with default: Date.now in the schema), but it's being overwritten to the current timestamp when the request status is reviewed at line 418. This destroys the original submission timestamp.

Add a separate reviewed_at field to the room_requests schema to record when the request was actually reviewed, alongside the existing reviewed_by field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/events.js` around lines 415 - 420, The current handler that
updates a room request (using event.room_requests.id(requestId) and setting
request.status, request.requested_at, and request.reviewed_by) incorrectly
overwrites the original submission timestamp; instead, stop updating
request.requested_at and add a separate reviewed_at field on the room_requests
schema and set request.reviewed_at = new Date() when reviewing; update the code
that assigns reviewed_by to also assign reviewed_at, and ensure the
room_requests schema includes reviewed_at (Date) so the original requested_at
(default: Date.now) remains unchanged.
backend/routes/positionRoutes.js (1)

93-93: ⚠️ Potential issue | 🟡 Minor

Debug console.log(req.body) left in production path.

req.body in this route carries user_id, position_id, and appointment_details. Remove before merging.

🐛 Proposed fix
-  console.log(req.body);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/positionRoutes.js` at line 93, Remove the stray
console.log(req.body) left in the route handler in positionRoutes.js — this
prints sensitive request payload (user_id, position_id, appointment_details) to
stdout; either delete the log line or replace it with a safe debug call (e.g.,
processLogger.debug) that omits or redacts sensitive fields before logging, and
ensure you only log non-sensitive identifiers if absolutely necessary in the
route handler where console.log(req.body) appears.
backend/controllers/dashboardController.js (1)

111-117: ⚠️ Potential issue | 🟠 Major

console.log(email) leaks PII to server logs.

User email is a personally-identifiable identifier. Logging it unconditionally in the CLUB_COORDINATOR branch violates GDPR/privacy best practices and can accumulate PII in log aggregation systems.

🛡️ Proposed fix
-  console.log(email);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/dashboardController.js` around lines 111 - 117, The
branch handling ROLES.CLUB_COORDINATOR currently logs the raw user email
(console.log(email)), leaking PII; remove that unconditional logging and instead
log only non-identifying context when OrganizationalUnit.findOne returns null
(e.g., a message including the role and request id or user id, or a
deterministic non-reversible hash of the email). Update the CLUB_COORDINATOR
case around the clubUnit lookup (the code using OrganizationalUnit.findOne and
the console.log(email) line) to delete the console.log(email) call and replace
it with a privacy-safe log (no raw email) or no log at all, or compute and log a
secure hash of the email if you need a traceable token.
backend/routes/feedbackRoutes.js (1)

50-50: ⚠️ Potential issue | 🔴 Critical

Typo: "ture" should be "true"is_anonymous string check is broken.

The comparison is_anonymous === "ture" will never match, so a string "true" value from the client will be treated as false.

🐛 Proposed fix
-      is_anonymous: is_anonymous === "ture" || is_anonymous === true,
+      is_anonymous: is_anonymous === "true" || is_anonymous === true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/feedbackRoutes.js` at line 50, The is_anonymous string
comparison in feedbackRoutes.js is misspelled ("ture") so string "true" from
clients is treated as false; update the check in the feedback submission handler
that sets is_anonymous (variable is_anonymous) to compare against "true" (and
consider normalizing input with .toLowerCase() or accepting both boolean and
string forms) so that is_anonymous: is_anonymous === "true" || is_anonymous ===
true correctly detects true values.
backend/routes/onboarding.js (1)

8-23: ⚠️ Potential issue | 🔴 Critical

No input validation — Program.trim() will throw a TypeError if Program is missing.

None of the destructured body fields (add_year, Program, discipline, mobile_no) are validated before use. If Program is undefined, line 20 crashes with Cannot read properties of undefined (reading 'trim'). Similarly, add_year and discipline are passed directly to the DB without checks.

Add validation (or use a Zod schema like other routes in this PR) before accessing these fields.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` around lines 8 - 23, The handler is using
Program.trim(), add_year, and discipline directly which can throw when Program
is undefined and allows invalid data into the DB; add a validation step (e.g.,
Zod schema or explicit guards) that requires Program (string), discipline
(string), and add_year (number or numeric-string) before proceeding, return 400
on validation failure, and then use the validated values in the
User.findByIdAndUpdate call (or use safe access like (Program||'').trim() only
as a fallback). Update the onboarding handler to validate/parse add_year (e.g.,
parseInt) and discipline, and ensure mobile_no defaults to "" when missing
before constructing personal_info.
backend/routes/profile.js (1)

106-111: ⚠️ Potential issue | 🟠 Major

PII logged to console — updatedDetails can contain personal data.

Lines 110–111 log userId and updatedDetails (which may include name, email, phone, date of birth, hostel, room number, social links). This is a compliance/privacy risk in production. Remove or redact these logs.

🛡️ Suggested fix
-    console.log("Received userId:", userId);
-    console.log("Received updatedDetails:", updatedDetails);
+    // Avoid logging PII — use structured logging with redaction in production
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 106 - 111, The code is logging
sensitive PII (userId and updatedDetails) in the route handler for
router.put("/updateStudentProfile", isAuthenticated, async (req, res) => { ...
}); remove those console.log calls (or replace them with non-PII telemetry) so
that userId and updatedDetails are not printed; if you need logging for
debugging, log a redact-safe summary (e.g., operation start and sanitized user
identifier or request id) rather than the full updatedDetails object and ensure
any retained logs do not include name, email, phone, DOB, hostel, room, or
social links.
backend/routes/auth.js (2)

220-234: ⚠️ Potential issue | 🟡 Minor

console.log(req.params) leaks token to logs.

Line 223 logs req.params, which includes the JWT reset token. An attacker with log access could use this to reset arbitrary passwords. Remove or redact.

🛡️ Fix
-    console.log(req.params);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 220 - 234, The handler for
router.get("/reset-password/:id/:token") is leaking sensitive data by logging
req.params (which contains the reset token); remove the console.log(req.params)
call or replace it with a safe log that only includes non-sensitive fields
(e.g., log req.params.id or a redacted placeholder), ensuring you do not log the
token value anywhere in the resetPassword verification flow (refer to the route
handler function and the req.params variable).

237-260: ⚠️ Potential issue | 🔴 Critical

Critical regression: user.setPassword() no longer exists after the schema rewrite.

The userSchema.js was refactored to use a custom pre("save") hook for password hashing instead of the passport-local-mongoose plugin. The reset-password route at line 247 still calls user.setPassword(password, ...), which will throw TypeError: user.setPassword is not a function at runtime and break password resets entirely.

Set the password directly and let the pre-save hook handle hashing:

Proposed fix
   try {
     jwt.verify(token, secret);
-    user.setPassword(password, async (error) => {
-      if (error) {
-        return res.status(500).json({ message: "Error resetting password" });
-      }
-      await user.save();
-      return res
-        .status(200)
-        .json({ message: "Password has been reset successfully" });
-    });
+    user.password = password;
+    await user.save(); // pre-save hook hashes the password
+    return res
+      .status(200)
+      .json({ message: "Password has been reset successfully" });
   } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/auth.js` around lines 237 - 260, The reset-password handler
still calls the removed method user.setPassword(password, ...); update the
router.post("/reset-password/:id/:token") flow to assign the plain password
directly to the User document (e.g. user.password = password), then await
user.save() so the schema's pre("save") hook performs hashing, and handle errors
from save with proper status responses; remove the setPassword callback usage
and keep the existing jwt.verify try/catch around the save operation.
🟠 Major comments (13)
backend/utils/batchValidate.js-3-3 (1)

3-3: ⚠️ Potential issue | 🟠 Major

ObjectId regex accepts non-hex characters — will allow invalid MongoDB ObjectIds.

MongoDB ObjectIds are 24-character hex strings (0-9, a-f). The current regex /^[0-9a-zA-Z]{24}$/ also accepts non-hex characters like g-z and G-Z, meaning invalid IDs will pass validation but fail at the database layer.

🐛 Proposed fix
-const zodObjectId = zod.string().regex(/^[0-9a-zA-Z]{24}$/, "Invalid ObjectId");
+const zodObjectId = zod.string().regex(/^[0-9a-fA-F]{24}$/, "Invalid ObjectId");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/utils/batchValidate.js` at line 3, The zodObjectId validator in
backend/utils/batchValidate.js currently allows non-hex characters; update the
regex used by zodObjectId so it only permits 24 hex characters (0-9 and a-f/A-F)
and keep the same "Invalid ObjectId" message; locate the zodObjectId constant
and replace its pattern with one that enforces exactly 24 hex digits.
frontend/src/services/auth.js-9-31 (1)

9-31: ⚠️ Potential issue | 🟠 Major

Inconsistent return shapes across service functions — callers can't safely consume these.

The functions in this module return wildly different shapes on success and error:

Function Success Error
fetchCredentials response.data throws
completeOnboarding response.data error.response (Axios response obj)
registerUser response (full Axios response!) error.response
loginUser res.data null

Specifically:

  • Line 27: registerUser returns the full Axios response object (with status, headers, config, etc.), while loginUser at line 38 returns res.data. This inconsistency means callers need to access response.data.data for registerUser vs result.someField for loginUser.
  • Lines 13-15 and 29-30: On error, completeOnboarding and registerUser return error.response (the raw Axios error response), which is a fundamentally different shape from the success path. Callers will need to check for status, data, etc.

Standardize all functions to return the same shape (e.g., always return response.data on success and null or a structured error object on error).

Proposed fix for registerUser
-    return response; 
+    return response.data; 
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` around lines 9 - 31, completeOnboarding and
registerUser return inconsistent shapes (full axios response or error.response)
which breaks callers; update both to always return response.data on success and
a consistent error sentinel (e.g., null) on failure. Specifically, in
completeOnboarding replace returning error.response with null, and in
registerUser return response.data (not the whole response) on success and return
null in the catch block; ensure both functions keep the try/catch but normalize
their return shapes to response.data / null so callers can consume results
uniformly.
frontend/src/Components/Dashboard/Dashboard.jsx-15-15 (1)

15-15: ⚠️ Potential issue | 🟠 Major

Replace <div>Kaushik</div> with a proper fallback.

This substitutes what was previously the Home component with a raw developer string. Any user navigating to an unrecognised route will see "Kaushik" instead of a real UI. Restore the original fallback or use a proper "Not Found" / redirect component.

🐛 Proposed fix
-    DashboardComponents[selectedRoute] || (() => <div>Kaushik</div>);
+    DashboardComponents[selectedRoute] || Home;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Dashboard/Dashboard.jsx` at line 15, The fallback for
unknown routes was replaced with a developer string; revert to a proper UI by
replacing the inline <div>Kaushik</div> in DashboardComponents[selectedRoute] ||
(() => <div>Kaushik</div>) with the original Home component or a
NotFound/Redirect component. Locate the DashboardComponents array/object and the
selectedRoute usage in Dashboard.jsx and return the Home component (or a
dedicated NotFound component) as the default render function so users see the
expected UI for unrecognized routes.
frontend/src/Components/Certificates/CertificatesList.jsx-22-52 (1)

22-52: ⚠️ Potential issue | 🟠 Major

Mock data will ship to production; api import is dead code until the endpoint is wired.

The real API call is commented out and the TODO is untracked. Merge this only after the /api/certificates/:id endpoint is ready and the mock block is removed, or gate the mock behind a dev-only flag so it can't accidentally reach production.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Certificates/CertificatesList.jsx` around lines 22 -
52, The component CertificatesList.jsx currently assigns hard-coded
mockCertificates and calls setCertificates, and the api import remains unused;
either remove the mock block and dead import before merging or gate the mock
data behind an environment/dev-only flag so it never runs in production. Replace
the mockCertificates + setCertificates path with the real API call to fetch
/api/certificates/:id when that endpoint exists (or wrap the mock assignment in
a check like process.env.NODE_ENV === 'development' / a feature flag) and clean
up the unused api import to prevent shipping dead code.
backend/controllers/eventControllers.js-6-9 (1)

6-9: ⚠️ Potential issue | 🟠 Major

Sort field updated_at (snake_case) doesn't match the selected/used field updatedAt (camelCase).

Mongoose's built-in timestamps use camelCase (updatedAt). Sorting by updated_at references a non-existent field, so the .limit(4) result order is effectively undefined — the "latest events" may not be the latest ones at all.

🐛 Proposed fix
-  .sort({updated_at: -1})
+  .sort({updatedAt: -1})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/controllers/eventControllers.js` around lines 6 - 9, The query that
builds latestEvents uses sort({updated_at: -1}) which references a non-existent
snake_case timestamp; change the sort to use Mongoose's camelCase timestamp
field (sort({updatedAt: -1})) so Event.find(...).sort(...) correctly orders by
the actual updatedAt field that is already being selected.
frontend/src/Components/Certificates/CertificatesList.jsx-14-64 (1)

14-64: ⚠️ Potential issue | 🟠 Major

Perpetual loading state when isUserLoggedIn is empty/null.

loading is initialised to true but the else branch (guard fails) never calls setLoading(false). Any user who lands on this page before isUserLoggedIn is populated — or without an active session — sees the "Loading certificates…" spinner indefinitely.

🐛 Proposed fix
    if (isUserLoggedIn && Object.keys(isUserLoggedIn).length > 0) {
      fetchCertificates();
+   } else {
+     setLoading(false);
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Certificates/CertificatesList.jsx` around lines 14 -
64, The useEffect/fetchCertificates flow leaves loading true when the guard if
(isUserLoggedIn && Object.keys(isUserLoggedIn).length > 0) fails; ensure
setLoading(false) is called in that case so the spinner doesn't show forever —
either initialize loading to false and only set true when starting
fetchCertificates, or add an else branch after the guard that calls
setLoading(false) (or explicitly setLoading(false) before returning) so that
setLoading is balanced whenever fetch is not invoked.
frontend/src/Components/Requests/Card.jsx-68-73 (1)

68-73: ⚠️ Potential issue | 🟠 Major

Object.assign(req, updatedRequest) mutates the parent's prop object in place.

Directly mutating props is a React anti-pattern — it bypasses React's rendering cycle, so the parent component won't know the data changed and won't re-render. This can lead to stale UI and subtle bugs.

Instead, lift the update to the parent via a callback, or maintain a local copy of the request in state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Card.jsx` around lines 68 - 73, handleSave
currently mutates the prop object via Object.assign(req, updatedRequest), which
mutates the parent's prop and breaks React's render model; update handleSave to
avoid in-place mutation by calling a parent-provided updater (e.g.,
onUpdateRequest or similar prop) with the updatedRequest, or update a local copy
stored in state (e.g., useState for request) and setRequest(updatedCopy) instead
of mutating req directly; locate the handleSave function and replace the direct
Object.assign(req, updatedRequest) use with a call to the parent callback or a
setState update to ensure React re-renders.
backend/config/passportConfig.js-25-25 (1)

25-25: ⚠️ Potential issue | 🟠 Major

User email (PII) logged to stdout.

Logging raw email addresses may create compliance concerns (GDPR/institutional policy). Consider logging a redacted form or omitting the address entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/config/passportConfig.js` at line 25, The console.log in the Google
OAuth flow is printing a user's raw email (profile.emails[0].value); remove or
replace this PII output in passportConfig.js by either omitting the address
entirely or logging a redacted/hash form (e.g., mask local-part or log only
domain) and use the app's logger (e.g., processLogger.warn/info) instead of
console.log to maintain consistent logging; update the OAuth verify callback
where console.log("Google OAuth blocked for: ", profile.emails[0].value) appears
to use the redacted value or no email.
frontend/src/Components/Requests/Requests.jsx-155-158 (1)

155-158: ⚠️ Potential issue | 🟠 Major

Missing key prop on <Card> inside .map().

React requires a unique key for list-rendered elements to avoid reconciliation bugs and console warnings. Each req already has an id field.

🐛 Proposed fix
-            <Card req={req} statusColor={statusColor} />
+            <Card key={req.id} req={req} statusColor={statusColor} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Requests.jsx` around lines 155 - 158, The
Card elements rendered in Requests.jsx inside the filteredRequests.map do not
include a unique key, causing React list-key warnings; update the mapping to
pass a key prop to the Card (e.g., key={req.id}) when rendering each <Card
req={req} statusColor={statusColor} /> so React can properly reconcile the list
(use req.id as the primary key and fall back to index only if id may be
missing).
backend/routes/onboarding.js-33-34 (1)

33-34: ⚠️ Potential issue | 🟠 Major

Internal error details leaked to client.

error.message can contain Mongoose validation errors, stack traces, or internal details. Return a generic message to the client and keep the detailed log server-side.

Proposed fix
     console.error("Onboarding failed:", error.message);
-    res.status(500).json({ message: error.message || "Onboarding failed"  });
+    res.status(500).json({ message: "Onboarding failed" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/onboarding.js` around lines 33 - 34, The route currently leaks
internal error details by returning error.message to the client; update the
onboarding error handling so that console.error (or your server logger) logs the
full error/stack (e.g., use console.error("Onboarding failed:", error) or
processLogger.error(error)) and change the response to a generic message like
res.status(500).json({ message: "Onboarding failed" }) instead of
res.status(500).json({ message: error.message || "Onboarding failed" }); locate
and update the console.error and res.status(...).json(...) calls inside the
onboarding route handler.
backend/routes/profile.js-17-31 (1)

17-31: ⚠️ Potential issue | 🟠 Major

Missing authorization check — any authenticated user can modify any profile.

All three routes (/photo-update, /photo-delete, /updateStudentProfile) accept an ID_No or userId from the request body/query and operate on that user's profile. There is no check that req.user owns the targeted profile. An authenticated user could update or delete another user's photo or profile data.

Compare the incoming ID_No/userId against req.user.user_id (or equivalent) before proceeding.

Also applies to: 80-90, 106-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 17 - 31, Add an ownership
authorization check to the profile routes so authenticated users cannot mutate
others' profiles: in the handlers for "/photo-update", "/photo-delete", and
"/updateStudentProfile" (the async route callbacks using ID_No or userId from
req.body/query), compare the supplied ID_No/userId with req.user.user_id (or the
canonical identifier on req.user) and immediately return a 403 Forbidden JSON
response if they differ; do this before calling User.findOne or performing any
update/delete and reuse the existing isAuthenticated middleware presence to
trust req.user.
backend/index.js-83-89 (1)

83-89: ⚠️ Potential issue | 🟠 Major

Server starts even if the DB connection fails.

connectDB() (per backend/config/db.js) catches and logs the error but does not re-throw or call process.exit(1). The await connectDB() here will resolve successfully even on failure, and the server will start listening without a working database. This leads to 500s on every request rather than a clear startup failure.

🛡️ Suggested fix — propagate DB errors

In backend/config/db.js, re-throw after logging:

   } catch (error) {
     console.error("MongoDB Connection Error:", error);
+    throw error;
   }

Or guard in index.js:

 (async function(){
-  await connectDB();
-  app.listen(process.env.PORT || 5000, () => {
-    console.log(`connected to port ${process.env.PORT || 5000}`);
-  });
+  try {
+    await connectDB();
+    app.listen(process.env.PORT || 5000, () => {
+      console.log(`connected to port ${process.env.PORT || 5000}`);
+    });
+  } catch (err) {
+    console.error("Failed to start server:", err);
+    process.exit(1);
+  }
 })();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` around lines 83 - 89, connectDB() currently swallows DB
errors so await connectDB() in the IIFE can resolve even on failure; either (A)
modify the implementation in backend/config/db.js so connectDB() re-throws the
error after logging (preserve current log but throw the caught error) or (B)
update the startup IIFE in backend/index.js to check the result and abort
startup on failure (wrap await connectDB() in try/catch and call process.exit(1)
on error before calling app.listen). Target the connectDB() function in
backend/config/db.js and the IIFE that calls await connectDB() / app.listen in
backend/index.js.
backend/models/userSchema.js-24-30 (1)

24-30: ⚠️ Potential issue | 🟠 Major

Password hash can leak in API responses — add select: false.

The password field lacks select: false, so it is included in every query result by default. In backend/routes/profile.js line 228, the full user object (including the hashed password) is returned in the updatedStudent response. Even hashed passwords should never leave the server.

Adding select: false ensures password is excluded unless explicitly requested (e.g., in login flows via .select("+password")).

Proposed fix
     password: {
       type: String,
       required: function () {
         return this.strategy === "local";
       },
       minLength: 8,
+      select: false,
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/userSchema.js` around lines 24 - 30, The password field on the
Mongoose schema (userSchema) is currently returned by default; add select: false
to the password schema definition so hashed passwords are excluded from query
results unless explicitly requested (e.g., with .select("+password") in login
flows). Update the password property in the userSchema (the password field
block) and ensure any code that needs the password (such as
authentication/login) uses .select("+password"); also check the profile route
where updatedStudent is returned to avoid sending the full user object with the
password.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (3)
frontend/src/hooks/useAuth.js (1)

21-29: Harden response-shape checks before calling handleLogin.

Current logic only checks falsy response; a truthy response without message leaves auth state ambiguous.

Suggested fix
 const response = await fetchCredentials();
-if(!response){
+if (!response?.success || !response?.message) {
   setIsUserLoggedIn(false);
+  setUserRole(null);
+  setIsOnboardingComplete(false);
   return;
 }
 
 const user = response.message;
 handleLogin(user);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useAuth.js` around lines 21 - 29, The code assumes
fetchCredentials() returns an object with a message property but only checks for
a falsy response; update the logic in the caller (around fetchCredentials,
response, handleLogin) to verify the response shape before calling handleLogin —
e.g., ensure response && typeof response.message === "object" (or expected
primitive/fields) and that required user fields exist; if the check fails, call
setIsUserLoggedIn(false) and return, otherwise extract the user and call
handleLogin(user). Make this change where response is used and reference the
symbols fetchCredentials, response.message, handleLogin, and setIsUserLoggedIn.
frontend/src/Components/common/Sidebar.jsx (1)

11-11: Avoid hardcoded nav truncation (slice(0, 14)).

Line 11 silently hides items beyond index 13. This is fragile for future menu additions (e.g., requests/certificates growth).

Proposed fix
-  const visibleNavItems = navItems.slice(0, 14);
+  const visibleNavItems = navItems;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/common/Sidebar.jsx` at line 11, The hardcoded
truncation visibleNavItems = navItems.slice(0, 14) in Sidebar.jsx silently hides
items; replace it with a configurable/dynamic approach: either read a
MAX_VISIBLE_NAV_ITEMS constant from config/env or a prop (e.g., maxVisible) and
use navItems.slice(0, maxVisible) or simply render all navItems and add a "Show
more" / collapsible behavior when length exceeds the threshold; update any
references to visibleNavItems accordingly (the navItems array and
visibleNavItems variable) and ensure unit/UI tests or prop types cover the new
maxVisible/config option.
frontend/src/services/auth.js (1)

24-35: Standardize service return/error contracts.

At Line 32 this returns full response, while other functions return .data or null. Mixing AxiosResponse | data | null | error.response makes callers brittle and caused control-flow issues nearby.

Suggested normalization pattern
 export async function registerUser(username, password, name) {
   try {
     const response = await api.post(`/auth/register`, {
       name,
       username,
       password,
     });
-    return response; 
+    return response.data;
   } catch (error) {
-    //console.error("Error obj is:",error.response);
-    return error.response;
+    throw error;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` around lines 24 - 35, The registerUser
function currently returns the full Axios response on success and returns
error.response on failure, which mixes response shapes; update registerUser to
return a normalized shape consistent with other auth services (e.g., return
response.data on success and null or a standardized error object on failure).
Locate registerUser in frontend/src/services/auth.js and change its success path
to return response.data and its catch path to return a consistent value (null or
an object like { error: error.response?.data || error.message }) so callers
receive a predictable contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/Components/common/Sidebar.jsx`:
- Line 2: The import path for the Logout component in Sidebar.jsx is wrong;
update the import statement that currently references "../Auth/Logout" to point
to the actual component location "../Logout" so the module resolves correctly;
locate the import line in Sidebar.jsx (the Logout import) and change the path,
then run a quick build or lint to verify the module is resolved.
- Line 82: The className on the Sidebar item contains conflicting text color
utilities (`text-zinc-400` and `text-white`); remove `text-zinc-400` and leave
`text-white` (in the template string used in the Sidebar component) so a single
text color governs the element, and check other Sidebar item className usages to
ensure only one text color utility is applied consistently.
- Around line 80-90: The Logout side-effect is currently triggered by mounting
<Logout /> inside the button subtree which ties the effect to rendering and
leaves loggingOut true on failures; instead, remove the <Logout /> mount and
implement an explicit async handler (e.g., handleLogout) called from the button
onClick that sets setLoggingOut(true), invokes the logout operation (either by
calling an exported logout function from the Logout component or by passing a
callback into the component), and then on success navigates/clears state and on
failure resets setLoggingOut(false) and surfaces an error so retry is possible;
update the Logout component to export a promise-returning logout function or
accept onComplete/onError callbacks so Sidebar can control the flow rather than
relying on mount effects.

In `@frontend/src/config/navbarConfig.js`:
- Line 83: Update the navbarConfig entry where the menu item object has key
"achievements" to change its label from "Achieve" to "Achievements" so the label
matches the key and reads clearly in navigation; locate the object with key
"achievements" (the entry containing label and icon, e.g., Trophy) and replace
the label string accordingly.
- Line 34: The navbar contains nav items with keys 'templates', 'batches',
'drafts', and 'submitted' that have no corresponding entries in the
dashboardComponents mapping, causing dead navigation; update the
dashboardComponents mapping to export components mapped to these keys (or
remove/disable the nav items) so each nav key in navbarConfig resolves to a real
component — locate the nav keys ('templates', 'batches', 'drafts', 'submitted')
in navbarConfig and add matching exports/entries in dashboardComponents (or
delete those keys from navbarConfig) to restore valid routing.
- Around line 69-71: The navbarConfig entries use plural keys that don't match
the dashboard registry and break coordinator routes; update the three objects in
the exported nav config (the entries with key "endorsements", "feedbacks", and
"pors") to use the registered keys "endorsement", "feedback", and "por"
respectively so they align with the dashboard registry and routing.

In `@frontend/src/hooks/useAuth.js`:
- Around line 27-29: Remove the debug log that prints the full user object to
the console to avoid exposing personal data: delete the console.log("User is:",
user) call in useAuth.js (the block that sets const user = response.message and
calls handleLogin(user)), or replace it with a non-sensitive status/logging
message (e.g., "user logged in") if you need an audit point; ensure
handleLogin(user) remains unchanged and no other code logs the user payload.
- Around line 11-15: The current logic in useAuth.js updates user role only when
userData.role exists, which can leave a stale role and mis-route authorization;
modify the update so that setUserRole is always called with userData.role when
present or a safe default (e.g., null or 'guest') when absent; specifically, in
the function handling userData (the block that calls setIsUserLoggedIn and
setIsOnboardingComplete), replace the conditional update of setUserRole with an
unconditional call that sets role to userData?.role ?? null (or your chosen
fail-closed default) so downstream checks in RoleProtectedRoute.js receive the
correct cleared value.

In `@frontend/src/services/auth.js`:
- Around line 39-43: The loginUser(username, password) signature was changed but
callers like the Login component still call loginUser(email, password); update
loginUser (in auth.js) to accept a neutral identifier (e.g., identifierOrEmail)
or support both names, and update or add an overload so it works with existing
callers; specifically, modify the loginUser function to accept (identifier,
password) or detect if the first arg is an email and forward it to
api.post("/auth/login", { username: identifier }) and then update Login.jsx
(which calls loginUser(email, password)) to pass its email prop unchanged, or
change all callers to pass username consistently so loginUser and callers
(loginUser, Login.jsx) match.
- Around line 15-21: The catch block in the onboarding call currently returns
error.response which makes the caller treat failures as resolved values; update
the catch in the async onboarding function (the try around
api.put(`/onboarding`, userData) in frontend/src/services/auth.js) to rethrow
the error (or return a rejected promise) instead of returning error.response so
callers like UserOnboarding.jsx can handle failures in their catch path;
optionally log the error before rethrowing for debugging.

---

Nitpick comments:
In `@frontend/src/Components/common/Sidebar.jsx`:
- Line 11: The hardcoded truncation visibleNavItems = navItems.slice(0, 14) in
Sidebar.jsx silently hides items; replace it with a configurable/dynamic
approach: either read a MAX_VISIBLE_NAV_ITEMS constant from config/env or a prop
(e.g., maxVisible) and use navItems.slice(0, maxVisible) or simply render all
navItems and add a "Show more" / collapsible behavior when length exceeds the
threshold; update any references to visibleNavItems accordingly (the navItems
array and visibleNavItems variable) and ensure unit/UI tests or prop types cover
the new maxVisible/config option.

In `@frontend/src/hooks/useAuth.js`:
- Around line 21-29: The code assumes fetchCredentials() returns an object with
a message property but only checks for a falsy response; update the logic in the
caller (around fetchCredentials, response, handleLogin) to verify the response
shape before calling handleLogin — e.g., ensure response && typeof
response.message === "object" (or expected primitive/fields) and that required
user fields exist; if the check fails, call setIsUserLoggedIn(false) and return,
otherwise extract the user and call handleLogin(user). Make this change where
response is used and reference the symbols fetchCredentials, response.message,
handleLogin, and setIsUserLoggedIn.

In `@frontend/src/services/auth.js`:
- Around line 24-35: The registerUser function currently returns the full Axios
response on success and returns error.response on failure, which mixes response
shapes; update registerUser to return a normalized shape consistent with other
auth services (e.g., return response.data on success and null or a standardized
error object on failure). Locate registerUser in frontend/src/services/auth.js
and change its success path to return response.data and its catch path to return
a consistent value (null or an object like { error: error.response?.data ||
error.message }) so callers receive a predictable contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 12a912d4-095e-4dbb-a3dd-d5076b10fd2d

📥 Commits

Reviewing files that changed from the base of the PR and between b726fc8 and 7263443.

📒 Files selected for processing (6)
  • frontend/src/Components/common/Sidebar.jsx
  • frontend/src/config/navbarConfig.js
  • frontend/src/hooks/useAuth.js
  • frontend/src/hooks/useSidebar.js
  • frontend/src/services/auth.js
  • frontend/src/utils/api.js
💤 Files with no reviewable changes (1)
  • frontend/src/hooks/useSidebar.js
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/utils/api.js

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/routes/profile.js (1)

110-111: ⚠️ Potential issue | 🟠 Major

Remove or sanitize debug logging of user data.

Logging updatedDetails directly can expose sensitive PII (email, phone, date of birth) in server logs. This poses a compliance/privacy risk (GDPR/CCPA). Either remove these logs or sanitize them to exclude sensitive fields.

Suggested fix
    const { userId, updatedDetails } = req.body;
-   console.log("Received userId:", userId);
-   console.log("Received updatedDetails:", updatedDetails);
+   console.log("Received profile update request for userId:", userId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 110 - 111, Remove or sanitize the
debug console logs that print user data: locate the console.log calls
referencing userId and updatedDetails in the profile route handler (the lines
logging "Received userId:" and "Received updatedDetails:") and either delete the
updatedDetails log or replace it with a sanitized summary that omits PII (e.g.,
remove keys like email, phone, dob, ssn, password) and only logs non-sensitive
fields or a boolean/status; keep a minimal non-PII log for userId if needed, or
remove both logs entirely for production.
♻️ Duplicate comments (1)
frontend/src/Components/common/Sidebar.jsx (1)

78-92: ⚠️ Potential issue | 🟠 Major

Logout failure leaves user stuck with no retry option.

Good improvements: disabled={loggingOut} prevents double-clicks, and <Logout /> is now rendered outside the button. However, the core issue remains: the Logout component (per frontend/src/Components/Auth/Logout.jsx) does not accept any callback props. If the logout API call fails, loggingOut remains true, the button stays disabled, and the user cannot retry.

Recommended approach
  1. Update Logout.jsx to accept onError and optionally onComplete callbacks:
-const Logout = () => {
+const Logout = ({ onError, onComplete }) => {
   // ...
       } catch (error) {
         console.error("Logout failed:", error);
+        onError?.();
       }
+      // optionally call onComplete on success if needed
  1. Update Sidebar to handle the error and reset state:
-        {loggingOut && <Logout />}
+        {loggingOut && <Logout onError={() => setLoggingOut(false)} />}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/common/Sidebar.jsx` around lines 78 - 92, The Logout
flow currently can leave the UI stuck because the Logout component (Logout)
never reports failures back to Sidebar; update the Logout component
(frontend/src/Components/Auth/Logout.jsx) to accept onError and onComplete
callback props and invoke onError(error) when the logout API fails and
onComplete() on any terminal path (success or final cleanup), and then update
Sidebar (component using setLoggingOut and loggingOut) to pass these callbacks:
pass onError={() => setLoggingOut(false)} (and optionally onComplete={() =>
setLoggingOut(false)}) so the button is re-enabled on failure or completion and
users can retry.
🧹 Nitpick comments (12)
backend/routes/profile.js (2)

139-159: Consider adding input validation for sensitive fields.

Fields like email and phone are updated without format validation. Invalid values could be stored in the database. Consider validating email format and phone number patterns before saving.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 139 - 159, The code updates sensitive
fields (name, email, phone, gender, date_of_birth, profilePic, cloudinaryUrl) on
the user.personal_info object without validating formats; add input validation
before assignment in the profile update handler: validate email using a robust
email check (e.g., validator.isEmail or a strict regex) and phone using a phone
pattern or library (e.g., validator.isMobilePhone with appropriate locale) and
reject or sanitize invalid inputs (return 400 with an error) rather than writing
them to user.personal_info; keep other fields (date_of_birth, gender) validated
for expected formats/values (e.g., ISO date, allowed gender values) before
assigning to user.personal_info.profilePic and user.personal_info.cloudinaryUrl
ensure they are valid URLs.

92-100: Consider returning a message when no photo exists to delete.

If cloudinaryUrl is empty/falsy, the route silently returns the current profilePic without indicating that no deletion occurred. Consider returning a different status or message to inform the client.

Suggested improvement
    // Delete from Cloudinary if exists
    if (user.personal_info.cloudinaryUrl) {
      await cloudinary.uploader.destroy(user.personal_info.cloudinaryUrl);
      user.personal_info.profilePic = "https://www.gravatar.com/avatar/?d=mp"; // Default photo
      user.personal_info.cloudinaryUrl = "";
      await user.save();
+    } else {
+      return res.status(200).json({ 
+        message: "No custom photo to delete",
+        profilePic: user.personal_info.profilePic 
+      });
    }

    res.json({ profilePic: user.personal_info.profilePic });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routes/profile.js` around lines 92 - 100, The route currently
silently returns the profilePic when user.personal_info.cloudinaryUrl is falsy;
add an explicit check before calling cloudinary.uploader.destroy and return a
clear status and message (e.g., res.status(404).json({ message: "No profile
photo to delete", profilePic: user.personal_info.profilePic })) if no
cloudinaryUrl exists; otherwise continue to call cloudinary.uploader.destroy,
set user.personal_info.profilePic to the default, clear
user.personal_info.cloudinaryUrl and await user.save() as currently implemented.
frontend/src/Components/common/Sidebar.jsx (1)

11-11: Consider extracting the magic number 14 into a named constant.

The hardcoded limit of 14 items is not immediately self-documenting. A named constant would clarify intent and make future adjustments easier.

+const MAX_VISIBLE_NAV_ITEMS = 14;
+
 const Sidebar = () => {
   const [loggingOut, setLoggingOut] = useState(false);
   const { navItems, selected, setSelected, isCollapsed, setIsCollapsed } = useSidebar();

-  const visibleNavItems = navItems.slice(0, 14);
+  const visibleNavItems = navItems.slice(0, MAX_VISIBLE_NAV_ITEMS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/common/Sidebar.jsx` at line 11, The slice limit 14 in
the visibleNavItems assignment is a magic number; extract it into a descriptive
constant (e.g., MAX_VISIBLE_NAV_ITEMS) and use that constant in the expression
that creates visibleNavItems (navItems.slice(0, MAX_VISIBLE_NAV_ITEMS)) so the
intent is clear and future changes are easier; add the constant near the top of
Sidebar.jsx or next to navItems so it’s easy to find.
frontend/src/services/auth.js (2)

1-3: Remove unused imports.

axios is commented out and toast is imported but never used in this file.

♻️ Proposed fix
-//import axios from "axios";
 import api from "../utils/api";
-import { toast } from "react-toastify";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` around lines 1 - 3, Remove the unused imports
in auth.js: delete the commented-out axios import line (axios) and remove the
unused `toast` import from "react-toastify" so only the actual `api` import
remains; ensure no other references to `axios` or `toast` exist in functions in
this file (e.g., any auth-related functions) before committing.

34-34: Remove commented-out debug log.

♻️ Proposed fix
   } catch (error) {
-    //console.error("Error obj is:",error.response);
     throw error
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/services/auth.js` at line 34, Remove the leftover commented
debug log (the commented-out console.error("Error obj is:",error.response);)
from frontend/src/services/auth.js; locate the commented line in the relevant
auth function (e.g., any error handling block such as login or request error
handlers) and delete it so no commented debug statements remain, and run
lint/format to ensure the file meets project style rules.
frontend/src/Components/Requests/Requests.jsx (2)

104-104: Missing navigate in useEffect dependency array.

The navigate function is used inside the effect but not listed in dependencies. While navigate from react-router is typically stable, ESLint rules for exhaustive deps would flag this.

♻️ Proposed fix
-  }, [isUserLoggedIn]);
+  }, [isUserLoggedIn, navigate]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Requests.jsx` at line 104, The effect in the
Requests component uses the navigate function but the dependency array only
includes isUserLoggedIn, which can trigger ESLint exhaustive-deps warnings;
update the useEffect dependency array in Requests.jsx to include navigate (e.g.,
useEffect(..., [isUserLoggedIn, navigate])) or, if navigate is intentionally
stable, explicitly document and silence the lint rule with a comment, ensuring
the reference to navigate in the effect is handled consistently.

1-1: Unused import: OctagonAlert.

OctagonAlert is imported but never used.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Requests.jsx` at line 1, The import list in
Requests.jsx includes an unused symbol OctagonAlert; remove OctagonAlert from
the named import list (import { Search, OctagonAlert, FileText } from
"lucide-react") or replace it by using OctagonAlert where intended to avoid the
unused-import lint error—update the import to import only Search and FileText
(or add the component usage) and run the linter to confirm the warning is
resolved.
frontend/src/Components/Requests/viewModal.jsx (1)

1-2: Remove unnecessary blank lines at file start.

The file begins with two empty lines before the imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/viewModal.jsx` around lines 1 - 2, Remove
the two leading blank lines at the top of
frontend/src/Components/Requests/viewModal.jsx so the file starts immediately
with its first import statement (e.g., import React.../other imports); ensure
there are no empty lines before the import block and save the file so the top of
the file begins with the import declarations.
frontend/src/Components/Requests/ui.jsx (1)

34-47: Consider adding a fallback for invalid color props.

If color is not one of the defined keys (amber, red, green), styles[color] will be undefined, resulting in no styling applied.

♻️ Proposed fix
 export const Pill = ({ label, color = "amber" }) => {
     const styles = {
         amber: "bg-yellow-100 text-yellow-800",
         red: "bg-red-100 text-red-700",
         green: "bg-green-100 text-green-700 ",
     };

     return (
-        <span className={`inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs tracking-wide font-semibold ${styles[color]}`}
+        <span className={`inline-flex items-center gap-1 px-3 py-1 rounded-full text-xs tracking-wide font-semibold ${styles[color] || styles.amber}`}
         >
             {label}
         </span>
     );
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/ui.jsx` around lines 34 - 47, Pill component
uses styles[color] directly so an invalid color prop yields undefined classes;
update Pill to validate or fallback by checking if color is a key in the styles
object (e.g., use styles[color] || styles.amber) or normalize the color before
use so the span always receives a valid class string; reference the Pill
function and the styles object to implement the fallback.
frontend/src/Components/Requests/editModal.jsx (1)

177-190: Save action proceeds regardless of validation or async result.

The Save button immediately calls onClose() after onSave(form). If onSave were async or could fail, the modal would close before the operation completes. Consider awaiting or confirming success before closing.

♻️ Proposed fix for safer save handling
-          <button onClick={() => { onSave(form); onClose(); }} style={{
+          <button onClick={async () => { 
+            try {
+              await onSave(form); 
+              onClose(); 
+            } catch (e) {
+              // handle error - modal stays open
+            }
+          }} style={{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/editModal.jsx` around lines 177 - 190, The
Save button currently calls onSave(form) and immediately onClose(), which closes
the modal even if onSave is async or fails; change the button handler to an
async handler that awaits onSave(form) (or checks its returned Promise/result)
and only call onClose() after a successful save, handle and surface errors
(e.g., set a local saving/error state to disable the button and show feedback),
and ensure you reference the click handler on the Save button, the onSave(form)
call and onClose() invocation when making these changes.
frontend/src/Components/Requests/Card.jsx (1)

1-1: Unused import: useState.

useState is imported but never used in this component.

♻️ Proposed fix
-import { useState } from "react";
 import { useAdminContext } from "../../context/AdminContext";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Requests/Card.jsx` at line 1, The import of useState
at the top of frontend/src/Components/Requests/Card.jsx is unused; remove the
named import useState from the import statement (or remove the entire import if
nothing else is imported) so the module no longer imports unused symbols and
ESLint warnings are resolved.
frontend/src/hooks/useAuth.js (1)

28-31: Remove commented-out debug logs.

These commented console.log statements are dead code and should be removed for cleanliness.

♻️ Proposed fix
         const user = response.message;
-        //console.log("User is:", user);
         handleLogin(user);
-          //console.log("User role:", user.role);
-          //console.log("Onboarding complete:", user.onboardingComplete);
       } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useAuth.js` around lines 28 - 31, Remove the dead
commented debug logs in useAuth (the commented console.log lines that reference
"User is:", "User role:", and "Onboarding complete:") so the handleLogin(user)
call stands alone; simply delete those commented lines to clean up the hook and
avoid leaving commented-out console.log statements in the useAuth.js
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/index.js`:
- Line 42: The app currently uses process.env.SESSION_SECRET (referenced as
secret: process.env.SESSION_SECRET) without validating it; add a startup check
immediately after require("dotenv").config() to ensure
process.env.SESSION_SECRET is set and throw a clear Error (e.g., "SESSION_SECRET
environment variable is required") if missing so the process fails fast and
avoids running with an undefined session secret.
- Around line 89-95: The current startup IIFE awaits connectDB() without
handling errors, so a rejection will become an unhandled promise rejection and
the server may still start; wrap the connectDB() call in a try/catch inside the
async IIFE (or before calling app.listen) and on catch call console.error with
the error and exit with process.exit(1) so the server doesn't start on DB
failure; also consider adding a process.on('unhandledRejection', ...) handler to
log and exit for any stray promise rejections.

In `@backend/models/certificateSchema.js`:
- Around line 6-9: The certificateSchema's eventIid field uses the wrong
Mongoose ref ("eventSchema") causing populate to return null; update the ref for
eventIid (the field defined with mongoose.Schema.Types.ObjectId named eventIid
in certificateSchema) to match the model name "Event" (the string used in
mongoose.model("Event", ...)) so populate('eventIid') works correctly.

In `@frontend/src/Components/common/Sidebar.jsx`:
- Around line 61-62: The conditional class strings in Sidebar.jsx use the old
Tailwind important prefix syntax (!rounded-2xl, !rounded-xl) which is
incompatible with Tailwind v4; update those class names used in the JSX
conditional (the ternary that returns "bg-white text-black font-medium
!rounded-2xl mb-1" or "text-zinc-400 hover:text-white hover:bg-zinc-600
!rounded-xl") to the v4 suffix form by moving the exclamation mark to the end of
the utility names (e.g., "rounded-2xl!" and "rounded-xl!") so the !important
modifier is applied.

In `@frontend/src/Components/Requests/Card.jsx`:
- Around line 104-105: The conditional rendering in Card.jsx uses
userRole.startsWith("GENSEC") which will throw if userRole is null/undefined;
update the expression to guard against null (e.g., use optional chaining
userRole?.startsWith("GENSEC") or a boolean check like (userRole &&
userRole.startsWith("GENSEC"))) so the overall condition becomes (userRole ===
"PRESIDENT" || userRole?.startsWith("GENSEC")) to prevent runtime TypeError
during render.
- Line 52: The inline style for the card border in the Requests Card component
has a CSS syntax typo: change the borderBottom value in the JSX (style={{
borderBottom: last ? "none" : "2px solidrgb(198, 194, 150)" }}) to include a
space before the rgb function so it reads "2px solid rgb(198, 194, 150)"; update
the expression in Card (Requests/RequestsCard component) accordingly to ensure
the border renders correctly.

In `@frontend/src/Components/Requests/editModal.jsx`:
- Around line 127-132: The number input's onChange currently uses
set("students") directly so e.target.value (a string) gets stored, changing
form.students from number to string; update the input's onChange to convert
e.target.value to a numeric type before calling the setter produced by
set("students") (e.g. parseInt/Number and handle empty string/null cases) so
form.students remains a number; locate the input element in editModal.jsx and
adjust the onChange wrapper around set("students") accordingly.

In `@frontend/src/Components/Requests/Requests.jsx`:
- Around line 42-49: The loop in requests.forEach is overwriting total instead
of accumulating approved student counts: replace the assignment total =
approved*req.students with an accumulation (e.g., total += req.students) so
total sums req.students for each approved request; ensure approved and total are
initialized before the loop and leave other branches (Rejected, pending)
unchanged.
- Line 14: The component Requests.jsx is incorrectly destructuring requestStatus
and setRequestStatus from useRequest(); instead, update the destructuring to
pull the individual values and setters provided by RequestContext (pending,
approved, rejected, total, setPending, setApproved, setRejected, setTotal) and
then replace any uses of requestStatus.pending / requestStatus.approved etc.
with the corresponding variables (pending, approved, rejected, total) and
replace calls to setRequestStatus(...) with the specific setters
(setPending(...), setApproved(...), setRejected(...), setTotal(...)); also
update the useEffect and any handlers in the Requests component to call those
individual setters instead of setRequestStatus so the component matches
RequestContext's API.

In `@frontend/src/Components/Requests/viewModal.jsx`:
- Around line 77-87: The close button in viewModal.jsx uses a dynamic Tailwind
class text-[${C.warmGray}] which Tailwind can't resolve at runtime; update the
button to apply the color via inline style or a CSS class instead: remove
text-[${C.warmGray}] and add a style prop like style={{ color: C.warmGray }} (or
compute a static Tailwind class if a known token exists), ensuring the X icon
(component X) inherits the color or receives the same style; keep the onClick
handler (onClose) and other classes untouched.
- Around line 126-141: The footer Close button in viewModal.jsx uses dynamic
Tailwind bracket classes (bg-[${C.text}] and text-[${C.cream}]) which won't be
processed; replace those with regular Tailwind classes or set the colors via
inline style: remove bg-[${C.text}] and text-[${C.cream}] from className and add
style entries for backgroundColor and color (e.g., add backgroundColor: C.text
and color: C.cream) while preserving the rest of the className and existing
style (fontWeight, fontSize); update the button that uses onClose accordingly so
the visual colors are applied at runtime.
- Around line 69-75: The header div uses a dynamic Tailwind class
`border-[${C.borderStrong}]` which Tailwind can't resolve at runtime; remove
that from the className and add an inline style to set the border color (e.g.
keep `border-b` in className and add style={{ borderColor: C.borderStrong }}).
Edit the header div in viewModal.jsx (the top header div JSX) to remove
`border-[${C.borderStrong}]` and add the style prop so the border color is
applied via the C.borderStrong value.
- Around line 57-67: The modal container in viewModal.jsx uses dynamic Tailwind
bracket classes bg-[${C.cream}] and border-[${C.borderStrong}] which won't be
picked up by Tailwind's purge; remove those dynamic bracket classes from the
template string in the div's className and instead set the colors via the inline
style object (e.g., add backgroundColor: C.cream and borderColor: C.borderStrong
to the existing style prop), keeping the rest of the static Tailwind classes
(w-[520px], rounded-[20px], overflow-hidden, border-[1.5px], shadow-..., etc.)
intact so Tailwind can detect them. Ensure you update the className in the same
div element in viewModal.jsx and keep the existing fontFamily style.
- Around line 19-52: The InfoTile component uses dynamic Tailwind class names
(e.g., bg-[${C.white}], text-[${C.text}], border-[${C.border}]) which Tailwind
won't detect; update InfoTile to remove those dynamic bracket classes and pass
the colors via inline style props instead (set style={{ backgroundColor:
C.white, color: C.text, borderColor: C.border }} on the outer div and/or inner
spans) while keeping the static Tailwind utility classes (rounded-[14px],
px-[20px], py-[16px], flex, etc.); ensure Icon usage (Icon size/ strokeWidth)
and the wide conditional class (col-span-full) remain unchanged.

---

Outside diff comments:
In `@backend/routes/profile.js`:
- Around line 110-111: Remove or sanitize the debug console logs that print user
data: locate the console.log calls referencing userId and updatedDetails in the
profile route handler (the lines logging "Received userId:" and "Received
updatedDetails:") and either delete the updatedDetails log or replace it with a
sanitized summary that omits PII (e.g., remove keys like email, phone, dob, ssn,
password) and only logs non-sensitive fields or a boolean/status; keep a minimal
non-PII log for userId if needed, or remove both logs entirely for production.

---

Duplicate comments:
In `@frontend/src/Components/common/Sidebar.jsx`:
- Around line 78-92: The Logout flow currently can leave the UI stuck because
the Logout component (Logout) never reports failures back to Sidebar; update the
Logout component (frontend/src/Components/Auth/Logout.jsx) to accept onError and
onComplete callback props and invoke onError(error) when the logout API fails
and onComplete() on any terminal path (success or final cleanup), and then
update Sidebar (component using setLoggingOut and loggingOut) to pass these
callbacks: pass onError={() => setLoggingOut(false)} (and optionally
onComplete={() => setLoggingOut(false)}) so the button is re-enabled on failure
or completion and users can retry.

---

Nitpick comments:
In `@backend/routes/profile.js`:
- Around line 139-159: The code updates sensitive fields (name, email, phone,
gender, date_of_birth, profilePic, cloudinaryUrl) on the user.personal_info
object without validating formats; add input validation before assignment in the
profile update handler: validate email using a robust email check (e.g.,
validator.isEmail or a strict regex) and phone using a phone pattern or library
(e.g., validator.isMobilePhone with appropriate locale) and reject or sanitize
invalid inputs (return 400 with an error) rather than writing them to
user.personal_info; keep other fields (date_of_birth, gender) validated for
expected formats/values (e.g., ISO date, allowed gender values) before assigning
to user.personal_info.profilePic and user.personal_info.cloudinaryUrl ensure
they are valid URLs.
- Around line 92-100: The route currently silently returns the profilePic when
user.personal_info.cloudinaryUrl is falsy; add an explicit check before calling
cloudinary.uploader.destroy and return a clear status and message (e.g.,
res.status(404).json({ message: "No profile photo to delete", profilePic:
user.personal_info.profilePic })) if no cloudinaryUrl exists; otherwise continue
to call cloudinary.uploader.destroy, set user.personal_info.profilePic to the
default, clear user.personal_info.cloudinaryUrl and await user.save() as
currently implemented.

In `@frontend/src/Components/common/Sidebar.jsx`:
- Line 11: The slice limit 14 in the visibleNavItems assignment is a magic
number; extract it into a descriptive constant (e.g., MAX_VISIBLE_NAV_ITEMS) and
use that constant in the expression that creates visibleNavItems
(navItems.slice(0, MAX_VISIBLE_NAV_ITEMS)) so the intent is clear and future
changes are easier; add the constant near the top of Sidebar.jsx or next to
navItems so it’s easy to find.

In `@frontend/src/Components/Requests/Card.jsx`:
- Line 1: The import of useState at the top of
frontend/src/Components/Requests/Card.jsx is unused; remove the named import
useState from the import statement (or remove the entire import if nothing else
is imported) so the module no longer imports unused symbols and ESLint warnings
are resolved.

In `@frontend/src/Components/Requests/editModal.jsx`:
- Around line 177-190: The Save button currently calls onSave(form) and
immediately onClose(), which closes the modal even if onSave is async or fails;
change the button handler to an async handler that awaits onSave(form) (or
checks its returned Promise/result) and only call onClose() after a successful
save, handle and surface errors (e.g., set a local saving/error state to disable
the button and show feedback), and ensure you reference the click handler on the
Save button, the onSave(form) call and onClose() invocation when making these
changes.

In `@frontend/src/Components/Requests/Requests.jsx`:
- Line 104: The effect in the Requests component uses the navigate function but
the dependency array only includes isUserLoggedIn, which can trigger ESLint
exhaustive-deps warnings; update the useEffect dependency array in Requests.jsx
to include navigate (e.g., useEffect(..., [isUserLoggedIn, navigate])) or, if
navigate is intentionally stable, explicitly document and silence the lint rule
with a comment, ensuring the reference to navigate in the effect is handled
consistently.
- Line 1: The import list in Requests.jsx includes an unused symbol
OctagonAlert; remove OctagonAlert from the named import list (import { Search,
OctagonAlert, FileText } from "lucide-react") or replace it by using
OctagonAlert where intended to avoid the unused-import lint error—update the
import to import only Search and FileText (or add the component usage) and run
the linter to confirm the warning is resolved.

In `@frontend/src/Components/Requests/ui.jsx`:
- Around line 34-47: Pill component uses styles[color] directly so an invalid
color prop yields undefined classes; update Pill to validate or fallback by
checking if color is a key in the styles object (e.g., use styles[color] ||
styles.amber) or normalize the color before use so the span always receives a
valid class string; reference the Pill function and the styles object to
implement the fallback.

In `@frontend/src/Components/Requests/viewModal.jsx`:
- Around line 1-2: Remove the two leading blank lines at the top of
frontend/src/Components/Requests/viewModal.jsx so the file starts immediately
with its first import statement (e.g., import React.../other imports); ensure
there are no empty lines before the import block and save the file so the top of
the file begins with the import declarations.

In `@frontend/src/hooks/useAuth.js`:
- Around line 28-31: Remove the dead commented debug logs in useAuth (the
commented console.log lines that reference "User is:", "User role:", and
"Onboarding complete:") so the handleLogin(user) call stands alone; simply
delete those commented lines to clean up the hook and avoid leaving
commented-out console.log statements in the useAuth.js implementation.

In `@frontend/src/services/auth.js`:
- Around line 1-3: Remove the unused imports in auth.js: delete the
commented-out axios import line (axios) and remove the unused `toast` import
from "react-toastify" so only the actual `api` import remains; ensure no other
references to `axios` or `toast` exist in functions in this file (e.g., any
auth-related functions) before committing.
- Line 34: Remove the leftover commented debug log (the commented-out
console.error("Error obj is:",error.response);) from
frontend/src/services/auth.js; locate the commented line in the relevant auth
function (e.g., any error handling block such as login or request error
handlers) and delete it so no commented debug statements remain, and run
lint/format to ensure the file meets project style rules.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 955cdec8-cf7f-4414-bf2e-4de706683fb5

📥 Commits

Reviewing files that changed from the base of the PR and between 7263443 and 7045720.

📒 Files selected for processing (15)
  • backend/index.js
  • backend/models/certificateSchema.js
  • backend/routes/analytics.js
  • backend/routes/orgUnit.js
  • backend/routes/profile.js
  • backend/utils/authValidate.js
  • frontend/src/Components/Requests/Card.jsx
  • frontend/src/Components/Requests/Requests.jsx
  • frontend/src/Components/Requests/editModal.jsx
  • frontend/src/Components/Requests/ui.jsx
  • frontend/src/Components/Requests/viewModal.jsx
  • frontend/src/Components/common/Sidebar.jsx
  • frontend/src/hooks/useAuth.js
  • frontend/src/pages/requestsPage.jsx
  • frontend/src/services/auth.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/routes/analytics.js
  • backend/utils/authValidate.js
  • backend/routes/orgUnit.js

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

♻️ Duplicate comments (2)
backend/index.js (1)

42-42: ⚠️ Potential issue | 🟠 Major

Validate that SESSION_SECRET environment variable exists.

If SESSION_SECRET is undefined, the session will operate without a proper secret, which is a security risk. Add validation at startup to ensure this required environment variable is set.

,

Suggested fix after Line 2
// Add after require("dotenv").config();
if (!process.env.SESSION_SECRET) {
  throw new Error("SESSION_SECRET environment variable is required");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/index.js` at line 42, Add a startup validation to ensure
process.env.SESSION_SECRET is defined before the app initializes sessions: after
loading env (e.g., after require("dotenv").config()) check
process.env.SESSION_SECRET and throw a clear Error if missing so the session
config line using secret: process.env.SESSION_SECRET cannot run with an
undefined value; this protects the session setup in backend/index.js from
starting without SESSION_SECRET.
frontend/src/config/navbarConfig.js (1)

32-33: ⚠️ Potential issue | 🟠 Major

templates and batches still point to missing dashboard entries.

frontend/src/config/dashboardComponents.js:30-53 registers requests and certificates, but not templates or batches, so these menu items still resolve to the fallback dashboard instead of a real page. This looks like the same unresolved issue raised in earlier review feedback.

Also applies to: 47-48, 61-68

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/config/navbarConfig.js` around lines 32 - 33, Navbar items with
key "templates" and "batches" in navbarConfig.js point to missing dashboard
pages; update the mapping in dashboardComponents.js (the registry that currently
registers "requests" and "certificates") to include corresponding components for
keys "templates" and "batches" or remove those keys from navbarConfig.js if
those pages are intentionally not implemented. Locate the registry
function/object in dashboardComponents.js and either add entries for "templates"
and "batches" that import/return the correct components, or delete the
"templates"/"batches" entries from navbarConfig.js (also check the other
mentioned navbar entries at lines referenced) so the nav items no longer resolve
to the fallback dashboard.
🧹 Nitpick comments (5)
backend/models/templateSchema.js (1)

12-20: Consider consistent enum casing.

The category enum uses uppercase values ("CULTURAL", "TECHNICAL") while status uses title case ("Draft", "Active"). This inconsistency may cause confusion when querying or filtering. Consider standardizing on one casing convention.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/models/templateSchema.js` around lines 12 - 20, The category and
status enums in templateSchema.js use inconsistent casing—category uses
uppercase values (category enum) while status uses title case (status enum and
default). Standardize both enums to the same convention (e.g., all uppercase or
all title case) by updating the enum arrays for category and status and adjust
the status default to match the chosen casing; ensure any code that relies on
these values (queries or seed data) is updated to the new casing as well.
frontend/src/Components/Batches/select.jsx (1)

3-15: Consider consolidating with Templates/select.jsx to avoid duplication.

This component is nearly identical to frontend/src/Components/Templates/select.jsx, differing only in the default option value ("ALL" vs "All"). Consider extracting a shared Select component that accepts the default value as a prop to reduce maintenance burden.

Also, using o as the key on Line 11 assumes all options are unique strings. If duplicates exist, React will warn about duplicate keys.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/select.jsx` around lines 3 - 15, The Select
component is duplicated with Templates/select.jsx and should be consolidated:
extract a shared Select component (exported as Select) that accepts a prop for
the default option value (e.g., defaultValue or defaultOptionValue) so callers
can pass "ALL" or "All", then replace both implementations with imports of the
shared component; also fix the options rendering in Select (inside the
options.map in Select) to use a stable unique key (e.g., combine index and value
or use a provided id property) instead of just `o` to avoid React duplicate key
warnings when option strings may repeat.
frontend/src/Components/Batches/modalDialog.jsx (2)

169-192: Suppressed dependency studentIds may cause stale data issues.

The useEffect depends only on open, but uses studentIds which is computed from form.students. If form.students changes while the panel is already open, the effect won't refetch with the new IDs. This may be intentional (only fetch on initial open), but the eslint suppression hides a potential data staleness issue.

Consider either including studentIds (or a stable reference like JSON.stringify(studentIds)) in the dependency array, or adding a comment explaining why refetching on student change is intentionally avoided.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/modalDialog.jsx` around lines 169 - 192, The
effect in modalDialog.jsx uses studentIds (derived from form.students) but only
depends on open, risking stale data; update the useEffect that calls
fetchBatchUsers to either include studentIds (or a stable representation like
JSON.stringify(studentIds) or form.students) in the dependency array so it
refetches when student selection changes, or add an explicit inline comment
above the useEffect explaining that skipping studentIds is intentional and why
(and keep the eslint-disable if retained); reference the useEffect, studentIds,
fetchBatchUsers, setDetails, setLocalSelected, open, and form.students when
making the change.

523-524: Simplify isViewOnly check.

The current check viewingBatch && Object.keys(viewingBatch).length > 0 is defensive, but since viewingBatch is already checked for truthiness first, you could simplify to just !!viewingBatch if an empty object is never passed. If empty objects are valid inputs, the current logic is fine.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/modalDialog.jsx` around lines 523 - 524, The
isViewOnly computation is overly defensive; replace the expression "viewingBatch
&& Object.keys(viewingBatch).length > 0" with a simple truthiness cast like
"!!viewingBatch" (or "Boolean(viewingBatch)") in modalDialog.jsx to simplify the
check for the viewingBatch variable (update the isViewOnly constant
declaration).
frontend/src/Components/Batches/ui.jsx (1)

169-251: Modal lacks keyboard accessibility.

The modal doesn't handle Escape key to close, doesn't trap focus within the dialog, and lacks ARIA attributes (role="dialog", aria-modal="true", aria-labelledby). This impacts keyboard and screen reader users.

Suggested accessibility improvements
 export function Modal({ open, onClose, title, children }) {
   if (!open) return null;
+
+  const handleKeyDown = (e) => {
+    if (e.key === "Escape") onClose();
+  };
+
   return (
     <div
+      role="dialog"
+      aria-modal="true"
+      aria-labelledby="modal-title"
+      onKeyDown={handleKeyDown}
       style={{
         position: "fixed",
         inset: 0,

And add id="modal-title" to the <h2> element.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/ui.jsx` around lines 169 - 251, The Modal
component currently lacks keyboard accessibility: update the Modal function to
add ARIA attributes (role="dialog", aria-modal="true", aria-labelledby pointing
to an id you add to the title element, e.g., id="modal-title"), wire an Escape
key handler that calls onClose, and implement focus management so focus is moved
into the dialog on open and restored on close and focus is trapped inside the
modal while open (handle Tab/Shift+Tab or use a small focus-trap utility).
Ensure the <h2> (title) receives id="modal-title" and that onClose remains used
for both backdrop click and Escape to close.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/controllers/certificateBatchController.js`:
- Around line 17-31: The payload sent to validateBatchSchema is misaligned: the
schema expects title (not name) and doesn’t validate action, causing invalid
rejections or unchecked workflow state; update the controller so the object
passed to validateBatchSchema uses title: name and include action in the parsed
object, and then enforce/derive lifecycleStatus and approvalStatus only after
validation; alternatively, extend validateBatchSchema to make users and
signatoryDetails conditional (required when action === "Submitted" but optional
for "Draft") and ensure validateBatchSchema is invoked with the action field so
validation can enforce those rules (references: validateBatchSchema, action,
lifecycleStatus, approvalStatus, users, signatoryDetails).
- Around line 113-115: The editBatch function is incomplete and incorrectly
destructures user id (it currently does const { id } = req.user._id) and never
sends a response; either remove editBatch or implement it: correctly obtain the
user id (e.g., const id = req.user._id or const { id } = req.user), perform the
intended batch update logic (use the existing batch service or DB methods used
by other handlers in this file), and ensure you send proper responses and error
handling via res.status(...).json(...) (or res.send) and next(err) on failure;
also update any exports to remove or include editBatch as appropriate so routing
won't reference a broken handler.

In `@backend/index.js`:
- Around line 93-94: The server startup uses app.listen(process.env.PORT ||
5000) so the runtime default port is 5000 but deployment and frontend configs
still expect 8000; update the deployment and client config to match by changing
the container port mapping in docker-compose.yml from 8000:8000 to 5000:5000 and
update frontend/.env.example to use http://localhost:5000 (or set
process.env.PORT to 8000 if you prefer keeping 8000)—ensure app.listen,
process.env.PORT, docker-compose.yml, and frontend/.env.example are consistent.

In `@backend/models/certificateSchema.js`:
- Around line 122-135: The partialFilterExpression on
certificateBatchSchema.index uses an unsupported $ne operator; update the index
definition in the certificateBatchSchema.index call so the predicate uses
supported operators (e.g., replace lifecycleStatus: { $ne: "Draft" } with
lifecycleStatus: { $in: ["Published", "Active", "Archived"] } or whichever
explicit lifecycle states your queries use) while keeping approvalStatus:
"Pending" and the indexed keys (approverIds, approvalStatus, lifecycleStatus,
currentApprovalLevel) unchanged so the index will build successfully.

In `@backend/seed.js`:
- Around line 620-779: seedNamedUsers() creates new active PositionHolder
entries (via PositionHolder.create) for positions already seeded earlier,
causing multiple active holders for the same Position (coordinatorPosition,
presidentPosition, gensecPosition). Before creating a PositionHolder for a given
position inside seedNamedUsers(), query PositionHolder for an existing active
holder with position_id equal to coordinatorPosition._id / presidentPosition._id
/ gensecPosition._id and only create a new PositionHolder if none exists (or
create it with status "inactive" or attach the existing holder to the new user
as appropriate); update the code paths that call PositionHolder.create to
perform this check and branch accordingly.
- Around line 620-779: The seedNamedUsers function unconditionally creates users
with fixed passwords (e.g., calls to User.create with password "password123" and
"kaushik123"), which is dangerous in non-dev environments; add an explicit
dev-only guard at the top of seedNamedUsers (check a dedicated env flag like
process.env.ENABLE_DEV_SEEDING === 'true' or ensure process.env.NODE_ENV ===
'development') and abort/skip seeding if the flag is not set, logging a clear
message, so the User.create and PositionHolder.create blocks only run when the
dev-seed flag is enabled.

In `@backend/services/organization.service.js`:
- Around line 10-31: getPresidentOrganization currently only follows parent
pointers and can accept non-council intermediates; update
getPresidentOrganization to validate unit types/hierarchy at each step: confirm
the input club is a Club (e.g., check club.type or club.hierarchy_level),
confirm councilObj returned by getOrganization(club.parent_unit_id) is actually
a Council (e.g., councilObj.type === 'council' or expected hierarchy_level), and
confirm presidentObj matches the President organization (compare
presidentObj.type or hierarchy_level and then _id to presidentOrg._id) before
returning; use the existing variables getOrganization, presidentOrg, councilObj
and presidentObj to locate and validate these checks.

In `@backend/services/template.service.js`:
- Around line 4-8: Change findTemplate to return 404 when no template exists and
validate/catch invalid ObjectId formats: before calling Template.findById(id)
validate id using mongoose.Types.ObjectId.isValid(id) (or catch
mongoose.CastError) and throw new HttpError(400, "Invalid template id") for bad
format; if Template.findById(id) returns null throw new HttpError(404, "Selected
template doesn't exist"); keep the function name findTemplate and the HttpError
usage consistent when updating error messages.

In `@frontend/src/Components/Batches/batchCard.jsx`:
- Around line 298-455: The list view in BatchCard is using different property
names and status logic (b.name, b.org, b.students, b.status) than the
card/backend DTO (eventId, users, initiatedBy, lifecycleStatus), causing badges
and ActionBtnList behavior to diverge; update BatchCard to consume the same
batch shape (use b.lifecycleStatus, b.users, b.initiatedBy, b.eventId) or add a
small mapper inside BatchCard to translate the incoming DTO into that canonical
shape, then replace status checks (e.g., b.status) with b.lifecycleStatus and
ensure the ActionBtnList conditions (onEdit, onDelete, onView, onDuplicate,
onArchive) match the state machine used by the card view/backend so the
displayed actions and StatusBadge (StatusBadge) remain consistent.
- Around line 72-73: The color selection currently uses Math.random()
(colorIndex and c derived from BATCH_COLORS) which causes flicker; replace the
random index with a deterministic hash of the batch identifier (use batch.id or
batch._id) to compute an integer index into BATCH_COLORS (e.g., hash/mod length)
so each batch consistently maps to the same palette; update the logic that
assigns colorIndex and c accordingly.
- Around line 44-60: ActionBtnList currently renders an unnamed icon-only button
which is inaccessible; update the component signature to accept a label prop
(e.g., function ActionBtnList({ icon: Icon, danger, onClick, label })) and set
the native button attributes type="button", aria-label={label} and
title={label}, preserving existing className and onClick behavior, then update
all call sites to pass a descriptive visible action name via the label prop.

In `@frontend/src/Components/Batches/ui.jsx`:
- Around line 103-106: CertThumb uses Math.random() during render causing color
to change on every re-render; replace this with a deterministic selection or
memoization: compute an index from stable batch data (e.g., hash or simple
char-code sum of batch._id or batch.title) and use that index to pick from
BATCH_COLORS (use colorIndex = computedIndex % BATCH_COLORS.length), or compute
and memoize the color with React.useMemo keyed on batch._id/title; update
CertThumb to use that stable color instead of Math.random().

In `@frontend/src/Components/Templates/createTemplate.jsx`:
- Around line 1-3: The Create component currently only renders static text and
provides no action; update it so clicking the Templates "Create" button opens
the create-template form: modify Create to accept an onClick prop (or hook into
your templates modal/context) and call that handler when activated, then update
the Templates usage (where <Create /> is rendered inside the clickable button)
to pass the button's onClick that triggers opening the create form (e.g.,
setCreateModalOpen(true) or call openCreateTemplateModal()). Target symbols: the
Create component and the parent usage in Template.jsx where <Create /> is
rendered.

In `@frontend/src/Components/Templates/select.jsx`:
- Around line 9-12: The component currently renders a hardcoded "All" option and
then maps the options array which may also include "All", causing duplicate
keys/entries; update the mapping logic used in the JSX (the options.map(...)
expression) to filter out the "All" string before rendering (e.g.,
options.filter(o => o !== "All').map(...)) so the single explicit <option
value="All">{placeholder}</option> remains unique and keys are no longer
duplicated; keep using the same key/value expressions and placeholder logic when
mapping the filtered list.

In `@frontend/src/Components/Templates/Template.jsx`:
- Around line 35-70: The component seeds fixture data shaped as
{id,name,modified,createdBy,color} but the API/backend model uses different
fields (e.g. title instead of name, createdBy is a ref, timestamps like
createdAt/updatedAt), so replace the hardcoded TEMPLATES in getTemplates with a
normalized mapping of the API response before calling setTemplates: fetch
/api/templates, map each template (use id/_id, name <- title, modified <-
updatedAt || createdAt, createdBy <- resolve to createdBy.name or createdBy._id
as available, preserve color or generate a default), and ensure any local
mutations call the backend to persist; keep using templates, getTemplates,
setTemplates, filtered and filters (useMemo) but feed them the normalized shape
so the rest of the component doesn't break.
- Around line 222-245: The View/Edit buttons in Template.jsx currently use noop
handlers (() => "") and list icons lack onClick handlers, so either wire them to
real actions or remove/disable them: replace the noop onClick for the grid
"View" and conditional "Edit" buttons with calls to the actual handlers (e.g.,
viewTemplate(t.id), editTemplate(t.id) or openTemplateModal(t.id)) and add
corresponding click handlers for the list icons, or disable/hide the controls
when no handler exists; ensure you reference the existing duplicate(t.id)
pattern and the userRole / t.name checks when deciding which controls to render
so behavior matches the grid/list variants.
- Around line 99-110: The icon-only toggle buttons (the ones calling
setView("grid") and setView("list") that render <LayoutGrid /> and <List />)
plus the clear-search and list action icon buttons need accessible labels: add
aria-label attributes with descriptive text (e.g., aria-label="Grid view",
aria-label="List view", aria-label="Clear search", or the specific action name
for each list action button) to each <button> rendering only an icon so
assistive tech announces their purpose; locate these in Template.jsx around the
setView handlers and the clear-search/list action button renderings and add the
appropriate aria-label strings.

In `@frontend/src/services/batch.js`:
- Around line 3-29: The helpers fetchBatchUsers, fetchBatches, and createBatch
currently swallow errors (fetchBatchUsers returns [] and the others return
undefined) which hides backend failures; update each function (fetchBatchUsers,
fetchBatches, createBatch) to stop returning empty/undefined on catch and
instead rethrow the caught error (or throw a new Error that includes the caught
error message) after optional logging so callers can distinguish
network/validation/auth failures from empty results and handle them
appropriately; ensure return types remain consistent by only returning
res.data.message on success and propagating errors on failure.

In `@frontend/src/services/events.js`:
- Around line 4-12: fetchEvents currently catches errors, logs them, and returns
nothing (implicit undefined), which breaks callers expecting an array; either
re-throw the caught error so callers can handle failures (replace the catch body
with throw err) or return a safe fallback like an empty array (return []), and
ensure the change is applied inside the fetchEvents function so callers calling
(await fetchEvents()).map(...) won't get a TypeError.

---

Duplicate comments:
In `@backend/index.js`:
- Line 42: Add a startup validation to ensure process.env.SESSION_SECRET is
defined before the app initializes sessions: after loading env (e.g., after
require("dotenv").config()) check process.env.SESSION_SECRET and throw a clear
Error if missing so the session config line using secret:
process.env.SESSION_SECRET cannot run with an undefined value; this protects the
session setup in backend/index.js from starting without SESSION_SECRET.

In `@frontend/src/config/navbarConfig.js`:
- Around line 32-33: Navbar items with key "templates" and "batches" in
navbarConfig.js point to missing dashboard pages; update the mapping in
dashboardComponents.js (the registry that currently registers "requests" and
"certificates") to include corresponding components for keys "templates" and
"batches" or remove those keys from navbarConfig.js if those pages are
intentionally not implemented. Locate the registry function/object in
dashboardComponents.js and either add entries for "templates" and "batches" that
import/return the correct components, or delete the "templates"/"batches"
entries from navbarConfig.js (also check the other mentioned navbar entries at
lines referenced) so the nav items no longer resolve to the fallback dashboard.

---

Nitpick comments:
In `@backend/models/templateSchema.js`:
- Around line 12-20: The category and status enums in templateSchema.js use
inconsistent casing—category uses uppercase values (category enum) while status
uses title case (status enum and default). Standardize both enums to the same
convention (e.g., all uppercase or all title case) by updating the enum arrays
for category and status and adjust the status default to match the chosen
casing; ensure any code that relies on these values (queries or seed data) is
updated to the new casing as well.

In `@frontend/src/Components/Batches/modalDialog.jsx`:
- Around line 169-192: The effect in modalDialog.jsx uses studentIds (derived
from form.students) but only depends on open, risking stale data; update the
useEffect that calls fetchBatchUsers to either include studentIds (or a stable
representation like JSON.stringify(studentIds) or form.students) in the
dependency array so it refetches when student selection changes, or add an
explicit inline comment above the useEffect explaining that skipping studentIds
is intentional and why (and keep the eslint-disable if retained); reference the
useEffect, studentIds, fetchBatchUsers, setDetails, setLocalSelected, open, and
form.students when making the change.
- Around line 523-524: The isViewOnly computation is overly defensive; replace
the expression "viewingBatch && Object.keys(viewingBatch).length > 0" with a
simple truthiness cast like "!!viewingBatch" (or "Boolean(viewingBatch)") in
modalDialog.jsx to simplify the check for the viewingBatch variable (update the
isViewOnly constant declaration).

In `@frontend/src/Components/Batches/select.jsx`:
- Around line 3-15: The Select component is duplicated with Templates/select.jsx
and should be consolidated: extract a shared Select component (exported as
Select) that accepts a prop for the default option value (e.g., defaultValue or
defaultOptionValue) so callers can pass "ALL" or "All", then replace both
implementations with imports of the shared component; also fix the options
rendering in Select (inside the options.map in Select) to use a stable unique
key (e.g., combine index and value or use a provided id property) instead of
just `o` to avoid React duplicate key warnings when option strings may repeat.

In `@frontend/src/Components/Batches/ui.jsx`:
- Around line 169-251: The Modal component currently lacks keyboard
accessibility: update the Modal function to add ARIA attributes (role="dialog",
aria-modal="true", aria-labelledby pointing to an id you add to the title
element, e.g., id="modal-title"), wire an Escape key handler that calls onClose,
and implement focus management so focus is moved into the dialog on open and
restored on close and focus is trapped inside the modal while open (handle
Tab/Shift+Tab or use a small focus-trap utility). Ensure the <h2> (title)
receives id="modal-title" and that onClose remains used for both backdrop click
and Escape to close.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa192dc9-2d8f-4498-8e76-c158cf28e313

📥 Commits

Reviewing files that changed from the base of the PR and between 7045720 and e774f14.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • backend/controllers/certificateBatchController.js
  • backend/controllers/certificateController.js
  • backend/index.js
  • backend/middlewares/authorizeRole.js
  • backend/models/certificateSchema.js
  • backend/models/eventSchema.js
  • backend/models/positionSchema.js
  • backend/models/templateSchema.js
  • backend/seed.js
  • backend/services/event.service.js
  • backend/services/organization.service.js
  • backend/services/template.service.js
  • backend/services/user.service.js
  • backend/utils/batchValidate.js
  • backend/utils/httpError.js
  • frontend/src/Components/Auth/RoleRedirect.jsx
  • frontend/src/Components/Batches/batchCard.jsx
  • frontend/src/Components/Batches/modalDialog.jsx
  • frontend/src/Components/Batches/select.jsx
  • frontend/src/Components/Batches/ui.jsx
  • frontend/src/Components/Requests/Card.jsx
  • frontend/src/Components/Templates/Template.jsx
  • frontend/src/Components/Templates/createTemplate.jsx
  • frontend/src/Components/Templates/select.jsx
  • frontend/src/Components/Templates/thumbnailPreview.jsx
  • frontend/src/config/navbarConfig.js
  • frontend/src/context/RequestContext.js
  • frontend/src/pages/batchesPage.jsx
  • frontend/src/routes/StudentRoutes.js
  • frontend/src/services/batch.js
  • frontend/src/services/events.js
✅ Files skipped from review due to trivial changes (3)
  • frontend/src/routes/StudentRoutes.js
  • backend/models/positionSchema.js
  • backend/utils/httpError.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/context/RequestContext.js
  • frontend/src/Components/Auth/RoleRedirect.jsx
  • backend/controllers/certificateController.js
  • backend/utils/batchValidate.js
  • frontend/src/Components/Requests/Card.jsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (5)
frontend/src/pages/templatesPage.jsx (1)

3-3: Remove stale commented import.

Line 3 is dead commented code and adds noise. Please remove it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/templatesPage.jsx` at line 3, Remove the stale commented
import of RequestProvider from templatesPage.jsx by deleting the line "//import
{ RequestProvider } from "../context/RequestContext.js";" so the file no longer
contains dead commented code; ensure no other code depends on RequestProvider in
the same file and run a quick lint/format after removal.
frontend/src/Components/Batches/batches.jsx (3)

73-75: Remove debug console.log statements.

These logging statements appear to be development artifacts and should be removed before merging.

🧹 Proposed fix
-        console.log("Batches is:", batches[0]);
-        console.log("Events is: ", events[0]);
-        console.log("Templates is: ", templates[0]);
         setBatches(batches);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/batches.jsx` around lines 73 - 75, Remove the
development console.log statements in batches.jsx that print batches[0],
events[0], and templates[0]; locate the log calls in the Batches component (the
three console.log lines shown) and either delete them or gate them behind a
debug flag/env check so no debug output remains in production code.

286-288: Move Google Fonts import out of component.

The inline <style> tag will be re-injected on every render. Consider moving the font import to index.html or a global CSS file for better performance and to avoid duplicate style tags.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/batches.jsx` around lines 286 - 288, The
inline <style> block in the Batches component (batches.jsx) injects the Google
Fonts import on every render; remove that <style> element from the component and
add the `@import` or <link href="https://fonts.googleapis.com/..."
rel="stylesheet"> to a global place (e.g., your app's index.html head or a
global CSS file like index.css) so the font is loaded once and not re-inserted
during component renders; update any component references to rely on the global
font instead.

12-15: Unused imports: CircleFadingPlus, Clock1, Eraser.

These icons are imported but never used in the component.

🧹 Proposed fix
   X,
   Info,
   SquareUser,
   Plus,
   Building2,
   FolderOpen,
-  CircleFadingPlus,
-  Clock1,
   CircleFadingArrowUp,
-  Eraser,
 } from "lucide-react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Batches/batches.jsx` around lines 12 - 15, Remove the
unused icon imports to clean up the module: delete CircleFadingPlus, Clock1, and
Eraser from the import list in frontend/src/Components/Batches/batches.jsx (or,
if they were intended to be used, add the missing usages inside the Batches
component where appropriate); ensure only actually used icons remain in the
import statement to prevent linter/build warnings.
frontend/src/Components/Certificates/CertificatesList.jsx (1)

145-156: Give the search input an accessible name.

Right now the placeholder is doing double duty as the label. Add a <label> or at least aria-label="Search certificates" so the filter stays discoverable for screen-reader users after they start typing.

Minimal accessibility fix
         {/**search bar */}
         <div className="relative flex-1 ">
+          <label htmlFor="certificate-search" className="sr-only">
+            Search certificates
+          </label>
           <Search
             className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
             size={20}
           />
           <input
+            id="certificate-search"
             type="text"
             placeholder="Search by event or issued by..."
             value={searchTerm}
             onChange={(e) => setSearchTerm(e.target.value)}
+            aria-label="Search certificates"
             className="w-full pl-10 pr-4 py-1.5 rounded-xl border-2 border-black bg-white text-black placeholder-gray-400 "
           />
         </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/Components/Certificates/CertificatesList.jsx` around lines 145 -
156, The search input in CertificatesList.jsx (the <input> bound to searchTerm
and onChange calling setSearchTerm) lacks an accessible name; add one by either
adding a visible <label> tied to that input (for attribute matching the input
id) or by adding aria-label="Search certificates" (or aria-labelledby pointing
to a nearby label) and ensure the Search icon remains decorative
(aria-hidden="true") so screen readers announce the purpose correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/Components/Batches/batches.jsx`:
- Around line 357-362: The placeholder text in the Batches component's search
input is wrong ("Search templates…"); update the input element inside the
Batches component (where value={search} and onChange={(e) =>
setSearch(e.target.value)}) to use the correct placeholder "Search batches…" to
reflect the component's purpose and fix the copy/paste error.
- Around line 97-113: The filter logic uses b.name for the search but the batch
objects and form use title (see form.title, openEdit/openView assigning
b.title), so replace the search check in the filtered computation to reference
b.title (safely null-checking it before calling toLowerCase) instead of b.name;
also remove the debug console.log("Filtered is:", filtered[0]) line. Update the
filter clause that defines matchSearch and ensure it uses search?.toLowerCase()
compared against (b.title || "").toLowerCase() to avoid runtime errors.
- Around line 434-438: Update the empty-state copy in the Batches component so
it references "batches" instead of "templates": locate the fallback paragraph
element in frontend/src/Components/Batches/batches.jsx (the <p> with className
"text-xs font-bold text-gray-400") and change its text from "No templates exist.
Create one today" to "No batches exist. Create one today" (or equivalent
batch-focused wording).
- Around line 149-166: The openEdit (and similarly openView) handler currently
dereferences nested properties like b.eventId.title,
b.eventId.organizing_unit_id.name and b.eventId.schedule.start which can be
undefined; update openEdit and openView to add null-safety by guarding on
b.eventId (early return or fallback) and using optional chaining/default values
for deeper fields (e.g., eventId?.title, organizing_unit_id?.name,
schedule?.start) before calling setForm and setViewingBatch, and ensure date
parsing handles missing schedule values by providing a sensible default or
skipping toLocaleDateString when undefined.
- Around line 249-282: The handlers deleteBatch, archiveBatch and dupBatch are
operating on b.id but the backend returns MongoDB _id, and there are no backend
endpoints to persist changes; fix by transforming fetched batches (from
fetchBatches) to map _id -> id when setting state (so BatchCard keys and
onDelete/onArchive/onDuplicate receive defined id values), update
deleteBatch/archiveBatch/dupBatch to operate on that id field (and ensure
setBatches uses the transformed id), and either call or add appropriate backend
APIs (or stub TODOs) to persist the delete/archive/duplicate operations instead
of only mutating local state.

In `@frontend/src/Components/Certificates/CertificatesList.jsx`:
- Around line 18-20: Replace the hardcoded mock certificate list in
CertificatesList.jsx with a real API call and normalize the response to match
the backend certificate schema: call the endpoint (e.g.,
api.get(`/api/certificates/${isUserLoggedIn._id}`)),
setCertificates(response.data) and map each returned certificate to the shape
your UI expects (populate fields used by the search, CertificateCard details,
and download filename from the real model properties or the populated batchId
object instead of mock fields like event, issuedBy, date); update any code that
reads event/issuedBy/date to use the correct backend field names (or batchId.*)
so search, card rendering, and download name work when switching from mocks to
live data.
- Around line 236-252: Validate and sanitize certificate.certificateUrl before
using it in the View and download handlers: ensure it has an allowed scheme
(e.g., startsWith('https://') or is a same-origin relative path) and
reject/ignore others; in the View onClick (the handler using window.open) open
via a safe anchor or call window.open and immediately null out opener (e.g.,
const w = window.open(..., "_blank"); if (w) w.opener = null) or prefer creating
an <a> with target="_blank" and rel="noopener noreferrer"; in the download
handler (the code creating link.href and link.download) validate the URL
similarly and sanitize the generated filename (based on certificate.event) to
remove unsafe characters before assigning link.download; apply these checks
around certificate.certificateUrl references in the component (the View button
onClick and the download button logic) so untrusted URLs are never opened or
downloaded directly.

In `@frontend/src/pages/templatesPage.jsx`:
- Line 19: The collapsed grid colEnd is off-by-one: change the ternary that sets
colEnd (the expression using isCollapsed ? 26 : 20 in templatesPage.jsx) to use
25 when collapsed so that after the +1 adjustment in Layout.jsx (where colEnd is
incremented before rendering) the grid end line matches the 25-column collapsed
layout; update the colEnd ternary to isCollapsed ? 25 : 20 to prevent an
implicit extra column.

---

Nitpick comments:
In `@frontend/src/Components/Batches/batches.jsx`:
- Around line 73-75: Remove the development console.log statements in
batches.jsx that print batches[0], events[0], and templates[0]; locate the log
calls in the Batches component (the three console.log lines shown) and either
delete them or gate them behind a debug flag/env check so no debug output
remains in production code.
- Around line 286-288: The inline <style> block in the Batches component
(batches.jsx) injects the Google Fonts import on every render; remove that
<style> element from the component and add the `@import` or <link
href="https://fonts.googleapis.com/..." rel="stylesheet"> to a global place
(e.g., your app's index.html head or a global CSS file like index.css) so the
font is loaded once and not re-inserted during component renders; update any
component references to rely on the global font instead.
- Around line 12-15: Remove the unused icon imports to clean up the module:
delete CircleFadingPlus, Clock1, and Eraser from the import list in
frontend/src/Components/Batches/batches.jsx (or, if they were intended to be
used, add the missing usages inside the Batches component where appropriate);
ensure only actually used icons remain in the import statement to prevent
linter/build warnings.

In `@frontend/src/Components/Certificates/CertificatesList.jsx`:
- Around line 145-156: The search input in CertificatesList.jsx (the <input>
bound to searchTerm and onChange calling setSearchTerm) lacks an accessible
name; add one by either adding a visible <label> tied to that input (for
attribute matching the input id) or by adding aria-label="Search certificates"
(or aria-labelledby pointing to a nearby label) and ensure the Search icon
remains decorative (aria-hidden="true") so screen readers announce the purpose
correctly.

In `@frontend/src/pages/templatesPage.jsx`:
- Line 3: Remove the stale commented import of RequestProvider from
templatesPage.jsx by deleting the line "//import { RequestProvider } from
"../context/RequestContext.js";" so the file no longer contains dead commented
code; ensure no other code depends on RequestProvider in the same file and run a
quick lint/format after removal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ada2f7d-4f42-4bb8-808a-3c1d8b979ce2

📥 Commits

Reviewing files that changed from the base of the PR and between e774f14 and 47887db.

📒 Files selected for processing (3)
  • frontend/src/Components/Batches/batches.jsx
  • frontend/src/Components/Certificates/CertificatesList.jsx
  • frontend/src/pages/templatesPage.jsx

@harshitap1305
Copy link
Member

@codewkaushik404 please resolve the conflicts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants